vircgroup.c 132.2 KB
Newer Older
1
/*
2
 * vircgroup.c: methods for managing control cgroups
3
 *
4
 * Copyright (C) 2010-2015 Red Hat, Inc.
5 6
 * Copyright IBM Corp. 2008
 *
O
Osier Yang 已提交
7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25 26
 *
 * Authors:
 *  Dan Smith <danms@us.ibm.com>
 */
#include <config.h>

#include <stdio.h>
27 28
#if defined HAVE_MNTENT_H && defined HAVE_SYS_MOUNT_H \
    && defined HAVE_GETMNTENT_R
29
# include <mntent.h>
30 31
# include <sys/mount.h>
#endif
32 33 34 35 36
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
37 38 39 40 41 42 43

#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>
#elif MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
#endif

44
#include <sys/types.h>
45
#include <signal.h>
46
#include <dirent.h>
M
Michal Privoznik 已提交
47
#include <unistd.h>
48

49 50 51
#define __VIR_CGROUP_ALLOW_INCLUDE_PRIV_H__
#include "vircgrouppriv.h"

52
#include "virutil.h"
53
#include "viralloc.h"
54
#include "virerror.h"
55
#include "virlog.h"
E
Eric Blake 已提交
56
#include "virfile.h"
57
#include "virhash.h"
58
#include "virhashcode.h"
59
#include "virstring.h"
60
#include "virsystemd.h"
61
#include "virtypedparam.h"
62
#include "virhostcpu.h"
63
#include "virthread.h"
64

65 66
VIR_LOG_INIT("util.cgroup");

67 68
#define CGROUP_MAX_VAL 512

69 70
#define VIR_FROM_THIS VIR_FROM_CGROUP

71
#define CGROUP_NB_TOTAL_CPU_STAT_PARAM 3
72
#define CGROUP_NB_PER_CPU_STAT_PARAM   1
73

74
#if defined(__linux__) && defined(HAVE_GETMNTENT_R) && \
75
    defined(_DIRENT_HAVE_D_TYPE) && defined(_SC_CLK_TCK)
76 77 78
# define VIR_CGROUP_SUPPORTED
#endif

79
VIR_ENUM_IMPL(virCgroupController, VIR_CGROUP_CONTROLLER_LAST,
R
Ryota Ozaki 已提交
80
              "cpu", "cpuacct", "cpuset", "memory", "devices",
81 82
              "freezer", "blkio", "net_cls", "perf_event",
              "name=systemd");
83

84 85 86 87 88 89 90 91
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;

E
Eric Blake 已提交
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/**
 * 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 "";
        }
    }
}


131
#ifdef VIR_CGROUP_SUPPORTED
E
Eric Blake 已提交
132 133
bool
virCgroupAvailable(void)
134
{
135
    bool ret = false;
136 137 138 139 140 141 142 143 144 145 146
    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) {
147 148 149 150
        /* 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=")) {
151 152 153 154 155 156 157 158 159
            ret = true;
            break;
        }
    }

    VIR_FORCE_FCLOSE(mounts);
    return ret;
}

E
Eric Blake 已提交
160 161 162 163 164 165

static int
virCgroupPartitionNeedsEscaping(const char *path)
{
    FILE *fp = NULL;
    int ret = 0;
166
    VIR_AUTOFREE(char *) line = NULL;
E
Eric Blake 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    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;
    }

225
 cleanup:
E
Eric Blake 已提交
226 227 228 229 230 231 232 233 234
    VIR_FORCE_FCLOSE(fp);
    return ret;
}


static int
virCgroupPartitionEscape(char **path)
{
    int rc;
235
    char *newstr = NULL;
E
Eric Blake 已提交
236 237 238 239

    if ((rc = virCgroupPartitionNeedsEscaping(*path)) <= 0)
        return rc;

240
    if (virAsprintf(&newstr, "_%s", *path) < 0)
E
Eric Blake 已提交
241 242
        return -1;

243 244 245
    VIR_FREE(*path);
    *path = newstr;

E
Eric Blake 已提交
246 247
    return 0;
}
E
Eric Blake 已提交
248 249


250
static bool
251 252 253
virCgroupValidateMachineGroup(virCgroupPtr group,
                              const char *name,
                              const char *drivername,
254
                              bool stripEmulatorSuffix,
255
                              const char *machinename)
256 257
{
    size_t i;
258 259 260 261
    VIR_AUTOFREE(char *) partname = NULL;
    VIR_AUTOFREE(char *) scopename_old = NULL;
    VIR_AUTOFREE(char *) scopename_new = NULL;
    VIR_AUTOFREE(char *) partmachinename = NULL;
262 263 264

    if (virAsprintf(&partname, "%s.libvirt-%s",
                    name, drivername) < 0)
265
        return false;
266 267

    if (virCgroupPartitionEscape(&partname) < 0)
268
        return false;
269

270 271 272 273
    if (machinename &&
        (virAsprintf(&partmachinename, "%s.libvirt-%s",
                     machinename, drivername) < 0 ||
         virCgroupPartitionEscape(&partmachinename) < 0))
274
        return false;
275

276
    if (!(scopename_old = virSystemdMakeScopeName(name, drivername, true)))
277
        return false;
278

279 280 281 282 283
    /* We should keep trying even if this failed */
    if (!machinename)
        virResetLastError();
    else if (!(scopename_new = virSystemdMakeScopeName(machinename,
                                                       drivername, false)))
284
        return false;
285 286

    if (virCgroupPartitionEscape(&scopename_old) < 0)
287
        return false;
288 289 290

    if (scopename_new &&
        virCgroupPartitionEscape(&scopename_new) < 0)
291
        return false;
292

293 294 295
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
        char *tmp;

296 297 298
        if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
            continue;

299 300 301 302 303
        if (!group->controllers[i].placement)
            continue;

        tmp = strrchr(group->controllers[i].placement, '/');
        if (!tmp)
304
            return false;
305 306 307 308 309 310 311 312 313

        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)
314
                return false;
315 316
        }

317 318 319
        tmp++;

        if (STRNEQ(tmp, name) &&
320
            STRNEQ_NULLABLE(tmp, machinename) &&
321
            STRNEQ(tmp, partname) &&
322
            STRNEQ_NULLABLE(tmp, partmachinename) &&
323 324
            STRNEQ(tmp, scopename_old) &&
            STRNEQ_NULLABLE(tmp, scopename_new)) {
E
Eric Blake 已提交
325
            VIR_DEBUG("Name '%s' for controller '%s' does not match "
326
                      "'%s', '%s', '%s', '%s' or '%s'",
E
Eric Blake 已提交
327
                      tmp, virCgroupControllerTypeToString(i),
328 329
                      name, NULLSTR(machinename), partname,
                      scopename_old, NULLSTR(scopename_new));
330
            return false;
331
        }
332 333
    }

334
    return true;
335
}
E
Eric Blake 已提交
336

L
Lai Jiangshan 已提交
337

E
Eric Blake 已提交
338 339 340
static int
virCgroupCopyMounts(virCgroupPtr group,
                    virCgroupPtr parent)
341
{
342
    size_t i;
343
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
344 345 346
        if (!parent->controllers[i].mountPoint)
            continue;

347 348 349
        if (VIR_STRDUP(group->controllers[i].mountPoint,
                       parent->controllers[i].mountPoint) < 0)
            return -1;
350

351 352 353
        if (VIR_STRDUP(group->controllers[i].linkPoint,
                       parent->controllers[i].linkPoint) < 0)
            return -1;
354 355 356 357
    }
    return 0;
}

E
Eric Blake 已提交
358

359 360 361 362
/*
 * Process /proc/mounts figuring out what controllers are
 * mounted and where
 */
363 364 365 366
int
virCgroupDetectMountsFromFile(virCgroupPtr group,
                              const char *path,
                              bool checkLinks)
367
{
368
    size_t i;
369
    FILE *mounts = NULL;
370 371
    struct mntent entry;
    char buf[CGROUP_MAX_VAL];
372
    int ret = -1;
373

374
    mounts = fopen(path, "r");
375
    if (mounts == NULL) {
376
        virReportSystemError(errno, _("Unable to open %s"), path);
377
        return -1;
378 379 380
    }

    while (getmntent_r(mounts, &entry, buf, sizeof(buf)) != NULL) {
381 382
        if (STRNEQ(entry.mnt_type, "cgroup"))
            continue;
383

384
        for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
385 386 387
            const char *typestr = virCgroupControllerTypeToString(i);
            int typelen = strlen(typestr);
            char *tmp = entry.mnt_opts;
388
            struct _virCgroupController *controller = &group->controllers[i];
389 390 391 392 393 394 395 396 397
            while (tmp) {
                char *next = strchr(tmp, ',');
                int len;
                if (next) {
                    len = next-tmp;
                    next++;
                } else {
                    len = strlen(tmp);
                }
398 399

                if (typelen == len && STREQLEN(typestr, tmp, len)) {
400 401 402
                    struct stat sb;
                    char *tmp2;

403 404 405 406 407 408 409 410 411 412
                    /* Note that the lines in /proc/mounts have the same
                     * order than the mount operations, and that there may
                     * be duplicates due to bind mounts. This means
                     * that the same mount point may be processed more than
                     * once. We need to save the results of the last one,
                     * and we need to be careful to release the memory used
                     * by previous processing. */
                    VIR_FREE(controller->mountPoint);
                    VIR_FREE(controller->linkPoint);
                    if (VIR_STRDUP(controller->mountPoint, entry.mnt_dir) < 0)
413
                        goto cleanup;
414 415 416

                    tmp2 = strrchr(entry.mnt_dir, '/');
                    if (!tmp2) {
417 418 419
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Missing '/' separator in cgroup mount '%s'"),
                                       entry.mnt_dir);
420
                        goto cleanup;
421
                    }
422

423 424
                    /* If it is a co-mount it has a filename like "cpu,cpuacct"
                     * and we must identify the symlink path */
425
                    if (checkLinks && strchr(tmp2 + 1, ',')) {
426 427
                        VIR_AUTOFREE(char *) linksrc = NULL;

428
                        *tmp2 = '\0';
429 430
                        if (virAsprintf(&linksrc, "%s/%s",
                                        entry.mnt_dir, typestr) < 0)
431
                            goto cleanup;
432 433 434 435 436 437 438
                        *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);
                            } else {
439
                                virReportSystemError(errno,
E
Eric Blake 已提交
440 441
                                                     _("Cannot stat %s"),
                                                     linksrc);
442
                                goto cleanup;
443 444 445 446 447 448
                            }
                        } else {
                            if (!S_ISLNK(sb.st_mode)) {
                                VIR_WARN("Expecting a symlink at %s for controller %s",
                                         linksrc, typestr);
                            } else {
449
                                controller->linkPoint = linksrc;
450
                                linksrc = NULL;
451 452 453 454
                            }
                        }
                    }
                }
455 456 457
                tmp = next;
            }
        }
458 459
    }

460 461
    ret = 0;
 cleanup:
462
    VIR_FORCE_FCLOSE(mounts);
463
    return ret;
464 465
}

466 467 468 469 470 471
static int
virCgroupDetectMounts(virCgroupPtr group)
{
    return virCgroupDetectMountsFromFile(group, "/proc/mounts", true);
}

472

E
Eric Blake 已提交
473 474 475 476
static int
virCgroupCopyPlacement(virCgroupPtr group,
                       const char *path,
                       virCgroupPtr parent)
477
{
478
    size_t i;
479
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
480 481 482
        if (!group->controllers[i].mountPoint)
            continue;

483 484 485
        if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
            continue;

486
        if (path[0] == '/') {
487 488
            if (VIR_STRDUP(group->controllers[i].placement, path) < 0)
                return -1;
489 490
        } else {
            /*
491 492 493
             * parent == "/" + path="" => "/"
             * parent == "/libvirt.service" + path == "" => "/libvirt.service"
             * parent == "/libvirt.service" + path == "foo" => "/libvirt.service/foo"
494 495 496 497 498 499 500
             */
            if (virAsprintf(&group->controllers[i].placement,
                            "%s%s%s",
                            parent->controllers[i].placement,
                            (STREQ(parent->controllers[i].placement, "/") ||
                             STREQ(path, "") ? "" : "/"),
                            path) < 0)
501
                return -1;
502 503 504 505 506 507 508
        }
    }

    return 0;
}


509
/*
510 511 512 513
 * virCgroupDetectPlacement:
 * @group: the group to process
 * @path: the relative path to append, not starting with '/'
 *
514 515
 * Process /proc/self/cgroup figuring out what cgroup
 * sub-path the current process is assigned to. ie not
516 517 518 519 520 521 522 523 524 525 526 527 528 529
 * 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.
530
 */
E
Eric Blake 已提交
531 532 533 534
static int
virCgroupDetectPlacement(virCgroupPtr group,
                         pid_t pid,
                         const char *path)
535
{
536
    size_t i;
537 538
    FILE *mapping  = NULL;
    char line[1024];
539
    int ret = -1;
540
    VIR_AUTOFREE(char *) procfile = NULL;
541

542
    VIR_DEBUG("Detecting placement for pid %lld path %s",
M
Michal Privoznik 已提交
543
              (long long) pid, path);
544 545 546 547
    if (pid == -1) {
        if (VIR_STRDUP(procfile, "/proc/self/cgroup") < 0)
            goto cleanup;
    } else {
M
Michal Privoznik 已提交
548 549
        if (virAsprintf(&procfile, "/proc/%lld/cgroup",
                        (long long) pid) < 0)
550 551 552 553
            goto cleanup;
    }

    mapping = fopen(procfile, "r");
554
    if (mapping == NULL) {
555 556 557 558
        virReportSystemError(errno,
                             _("Unable to open '%s'"),
                             procfile);
        goto cleanup;
559 560
    }

561 562
    while (fgets(line, sizeof(line), mapping) != NULL) {
        char *controllers = strchr(line, ':');
563 564
        char *selfpath = controllers ? strchr(controllers + 1, ':') : NULL;
        char *nl = selfpath ? strchr(selfpath, '\n') : NULL;
565

566
        if (!controllers || !selfpath)
567 568 569 570 571
            continue;

        if (nl)
            *nl = '\0';

572
        *selfpath = '\0';
573
        controllers++;
574
        selfpath++;
575

576
        for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
577 578 579
            const char *typestr = virCgroupControllerTypeToString(i);
            int typelen = strlen(typestr);
            char *tmp = controllers;
580

581 582 583 584
            while (tmp) {
                char *next = strchr(tmp, ',');
                int len;
                if (next) {
585
                    len = next - tmp;
586 587 588 589
                    next++;
                } else {
                    len = strlen(tmp);
                }
590 591

                /*
592 593 594
                 * selfpath == "/" + path="" -> "/"
                 * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
                 * selfpath == "/libvirt.service" + path == "foo" -> "/libvirt.service/foo"
595
                 */
596
                if (typelen == len && STREQLEN(typestr, tmp, len) &&
597 598 599 600 601 602 603 604 605 606 607 608 609 610
                    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;
                    }
611
                }
612 613 614 615 616 617

                tmp = next;
            }
        }
    }

618
    ret = 0;
619

620
 cleanup:
621
    VIR_FORCE_FCLOSE(mapping);
622
    return ret;
623 624
}

E
Eric Blake 已提交
625 626 627 628 629 630 631

static int
virCgroupDetect(virCgroupPtr group,
                pid_t pid,
                int controllers,
                const char *path,
                virCgroupPtr parent)
632
{
633 634
    size_t i;
    size_t j;
635 636
    VIR_DEBUG("group=%p controllers=%d path=%s parent=%p",
              group, controllers, path, parent);
637

638 639 640 641 642 643
    if (parent) {
        if (virCgroupCopyMounts(group, parent) < 0)
            return -1;
    } else {
        if (virCgroupDetectMounts(group) < 0)
            return -1;
644 645
    }

646
    if (controllers >= 0) {
647
        VIR_DEBUG("Filtering controllers %d", controllers);
648
        /* First mark requested but non-existing controllers to be ignored */
649
        for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
650
            if (((1 << i) & controllers)) {
651
                /* Remove non-existent controllers  */
652
                if (!group->controllers[i].mountPoint) {
653
                    VIR_DEBUG("Requested controller '%s' not mounted, ignoring",
654
                              virCgroupControllerTypeToString(i));
655
                    controllers &= ~(1 << i);
656
                }
657 658 659 660 661 662 663 664 665
            }
        }
        for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
            VIR_DEBUG("Controller '%s' wanted=%s, mount='%s'",
                      virCgroupControllerTypeToString(i),
                      (1 << i) & controllers ? "yes" : "no",
                      NULLSTR(group->controllers[i].mountPoint));
            if (!((1 << i) & controllers) &&
                group->controllers[i].mountPoint) {
666 667
                /* Check whether a request to disable a controller
                 * clashes with co-mounting of controllers */
668
                for (j = 0; j < VIR_CGROUP_CONTROLLER_LAST; j++) {
669 670 671 672 673 674 675
                    if (j == i)
                        continue;
                    if (!((1 << j) & controllers))
                        continue;

                    if (STREQ_NULLABLE(group->controllers[i].mountPoint,
                                       group->controllers[j].mountPoint)) {
676 677 678 679 680
                        virReportSystemError(EINVAL,
                                             _("Controller '%s' is not wanted, but '%s' is co-mounted"),
                                             virCgroupControllerTypeToString(i),
                                             virCgroupControllerTypeToString(j));
                        return -1;
681 682 683 684 685 686 687 688
                    }
                }
                VIR_FREE(group->controllers[i].mountPoint);
            }
        }
    } else {
        VIR_DEBUG("Auto-detecting controllers");
        controllers = 0;
689
        for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
690 691 692 693 694 695 696
            VIR_DEBUG("Controller '%s' present=%s",
                      virCgroupControllerTypeToString(i),
                      group->controllers[i].mountPoint ? "yes" : "no");
            if (group->controllers[i].mountPoint == NULL)
                continue;
            controllers |= (1 << i);
        }
697
    }
698

699
    /* Check that at least 1 controller is available */
700
    if (!controllers) {
701 702 703
        virReportSystemError(ENXIO, "%s",
                             _("At least one cgroup controller is required"));
        return -1;
704
    }
705

706 707 708 709 710 711 712 713 714 715
    /* 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;
716

717 718 719 720
    /* 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;
721

722 723 724 725 726 727
        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;
728
        }
729

M
Michal Privoznik 已提交
730 731
        VIR_DEBUG("Detected mount/mapping %zu:%s at %s in %s for pid %lld",
                  i,
732 733
                  virCgroupControllerTypeToString(i),
                  group->controllers[i].mountPoint,
734
                  group->controllers[i].placement,
M
Michal Privoznik 已提交
735
                  (long long) pid);
736 737
    }

738
    return 0;
739 740
}

741

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
static char *
virCgroupGetBlockDevString(const char *path)
{
    char *ret = NULL;
    struct stat sb;

    if (stat(path, &sb) < 0) {
        virReportSystemError(errno,
                             _("Path '%s' is not accessible"),
                             path);
        return NULL;
    }

    if (!S_ISBLK(sb.st_mode)) {
        virReportSystemError(EINVAL,
                             _("Path '%s' must be a block device"),
                             path);
        return NULL;
    }

    /* Automatically append space after the string since all callers
     * use it anyway */
    if (virAsprintf(&ret, "%d:%d ", major(sb.st_rdev), minor(sb.st_rdev)) < 0)
        return NULL;

    return ret;
}


E
Eric Blake 已提交
771 772 773 774 775
static int
virCgroupSetValueStr(virCgroupPtr group,
                     int controller,
                     const char *key,
                     const char *value)
776
{
777
    VIR_AUTOFREE(char *) keypath = NULL;
778
    char *tmp = NULL;
779

780 781
    if (virCgroupPathOfController(group, controller, key, &keypath) < 0)
        return -1;
782

783
    VIR_DEBUG("Set value '%s' to '%s'", keypath, value);
784
    if (virFileWriteStr(keypath, value, 0) < 0) {
785 786 787 788 789
        if (errno == EINVAL &&
            (tmp = strrchr(keypath, '/'))) {
            virReportSystemError(errno,
                                 _("Invalid value '%s' for '%s'"),
                                 value, tmp + 1);
790
            return -1;
791
        }
792 793
        virReportSystemError(errno,
                             _("Unable to write to '%s'"), keypath);
794
        return -1;
795 796
    }

797
    return 0;
798 799
}

E
Eric Blake 已提交
800 801 802 803 804 805

static int
virCgroupGetValueStr(virCgroupPtr group,
                     int controller,
                     const char *key,
                     char **value)
806
{
807 808
    VIR_AUTOFREE(char *) keypath = NULL;
    int rc;
809

810
    *value = NULL;
811

812 813
    if (virCgroupPathOfController(group, controller, key, &keypath) < 0)
        return -1;
814

815
    VIR_DEBUG("Get value %s", keypath);
816

817 818 819
    if ((rc = virFileReadAll(keypath, 1024*1024, value)) < 0) {
        virReportSystemError(errno,
                             _("Unable to read from '%s'"), keypath);
820
        return -1;
821 822
    }

823 824 825
    /* Terminated with '\n' has sometimes harmful effects to the caller */
    if (rc > 0 && (*value)[rc - 1] == '\n')
        (*value)[rc - 1] = '\0';
826

827
    return 0;
828 829
}

E
Eric Blake 已提交
830

831 832 833 834 835 836 837
static int
virCgroupGetValueForBlkDev(virCgroupPtr group,
                           int controller,
                           const char *key,
                           const char *path,
                           char **value)
{
838 839
    VIR_AUTOFREE(char *) prefix = NULL;
    VIR_AUTOFREE(char *) str = NULL;
840 841
    char **lines = NULL;
    int ret = -1;
842 843

    if (virCgroupGetValueStr(group, controller, key, &str) < 0)
844
        goto error;
845 846

    if (!(prefix = virCgroupGetBlockDevString(path)))
847
        goto error;
848 849

    if (!(lines = virStringSplit(str, "\n", -1)))
850
        goto error;
851

852
    if (VIR_STRDUP(*value, virStringListGetFirstWithPrefix(lines, prefix)) < 0)
853
        goto error;
854

855 856 857 858
    ret = 0;
 error:
    virStringListFree(lines);
    return ret;
859 860 861
}


E
Eric Blake 已提交
862 863 864 865 866
static int
virCgroupSetValueU64(virCgroupPtr group,
                     int controller,
                     const char *key,
                     unsigned long long int value)
867
{
868
    VIR_AUTOFREE(char *) strval = NULL;
869

870 871
    if (virAsprintf(&strval, "%llu", value) < 0)
        return -1;
872

873
    return virCgroupSetValueStr(group, controller, key, strval);
874 875 876
}


E
Eric Blake 已提交
877 878 879 880 881
static int
virCgroupSetValueI64(virCgroupPtr group,
                     int controller,
                     const char *key,
                     long long int value)
882
{
883
    VIR_AUTOFREE(char *) strval = NULL;
884

885 886
    if (virAsprintf(&strval, "%lld", value) < 0)
        return -1;
887

888
    return virCgroupSetValueStr(group, controller, key, strval);
889 890
}

E
Eric Blake 已提交
891 892 893 894 895 896

static int
virCgroupGetValueI64(virCgroupPtr group,
                     int controller,
                     const char *key,
                     long long int *value)
897
{
898
    VIR_AUTOFREE(char *) strval = NULL;
899

900
    if (virCgroupGetValueStr(group, controller, key, &strval) < 0)
901
        return -1;
902

903 904 905 906
    if (virStrToLong_ll(strval, NULL, 10, value) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       strval);
907
        return -1;
908
    }
909

910
    return 0;
911 912
}

E
Eric Blake 已提交
913 914 915 916 917 918

static int
virCgroupGetValueU64(virCgroupPtr group,
                     int controller,
                     const char *key,
                     unsigned long long int *value)
919
{
920
    VIR_AUTOFREE(char *) strval = NULL;
921

922
    if (virCgroupGetValueStr(group, controller, key, &strval) < 0)
923
        return -1;
924

925 926 927 928
    if (virStrToLong_ull(strval, NULL, 10, value) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       strval);
929
        return -1;
930
    }
931

932
    return 0;
933 934 935
}


E
Eric Blake 已提交
936 937
static int
virCgroupCpuSetInherit(virCgroupPtr parent, virCgroupPtr group)
938
{
939
    size_t i;
940 941 942
    const char *inherit_values[] = {
        "cpuset.cpus",
        "cpuset.mems",
943
        "cpuset.memory_migrate",
944 945
    };

946
    VIR_DEBUG("Setting up inheritance %s -> %s", parent->path, group->path);
947
    for (i = 0; i < ARRAY_CARDINALITY(inherit_values); i++) {
948
        VIR_AUTOFREE(char *) value = NULL;
949

950 951 952 953
        if (virCgroupGetValueStr(parent,
                                 VIR_CGROUP_CONTROLLER_CPUSET,
                                 inherit_values[i],
                                 &value) < 0)
954
            return -1;
955 956 957

        VIR_DEBUG("Inherit %s = %s", inherit_values[i], value);

958 959 960
        if (virCgroupSetValueStr(group,
                                 VIR_CGROUP_CONTROLLER_CPUSET,
                                 inherit_values[i],
961
                                 value) < 0)
962
            return -1;
963 964
    }

965
    return 0;
966 967
}

E
Eric Blake 已提交
968 969 970

static int
virCgroupSetMemoryUseHierarchy(virCgroupPtr group)
971 972 973 974
{
    unsigned long long value;
    const char *filename = "memory.use_hierarchy";

975 976 977
    if (virCgroupGetValueU64(group,
                             VIR_CGROUP_CONTROLLER_MEMORY,
                             filename, &value) < 0)
978
        return -1;
979 980 981 982 983 984

    /* Setting twice causes error, so if already enabled, skip setting */
    if (value == 1)
        return 0;

    VIR_DEBUG("Setting up %s/%s", group->path, filename);
985 986 987
    if (virCgroupSetValueU64(group,
                             VIR_CGROUP_CONTROLLER_MEMORY,
                             filename, 1) < 0)
988
        return -1;
989

990
    return 0;
991 992
}

E
Eric Blake 已提交
993 994 995 996 997 998

static int
virCgroupMakeGroup(virCgroupPtr parent,
                   virCgroupPtr group,
                   bool create,
                   unsigned int flags)
999
{
1000
    size_t i;
1001

1002
    VIR_DEBUG("Make group %s", group->path);
1003
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
1004
        VIR_AUTOFREE(char *) path = NULL;
1005

1006 1007 1008 1009 1010 1011
        /* We must never mkdir() in systemd's hierarchy */
        if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
            VIR_DEBUG("Not creating systemd controller group");
            continue;
        }

1012
        /* Skip over controllers that aren't mounted */
1013 1014 1015
        if (!group->controllers[i].mountPoint) {
            VIR_DEBUG("Skipping unmounted controller %s",
                      virCgroupControllerTypeToString(i));
1016
            continue;
1017
        }
1018

1019
        if (virCgroupPathOfController(group, i, "", &path) < 0)
1020
            return -1;
1021

1022 1023 1024
        /* As of Feb 2011, clang can't see that the above function
         * call did not modify group. */
        sa_assert(group->controllers[i].mountPoint);
1025

1026
        VIR_DEBUG("Make controller %s", path);
1027
        if (!virFileExists(path)) {
1028 1029
            if (!create ||
                mkdir(path, 0755) < 0) {
1030
                if (errno == EEXIST)
1031
                    continue;
1032 1033 1034 1035 1036
                /* 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) {
1037
                    VIR_DEBUG("Ignoring mkdir failure with blkio controller. Kernel probably too old");
1038 1039 1040
                    VIR_FREE(group->controllers[i].mountPoint);
                    continue;
                } else {
1041 1042 1043
                    virReportSystemError(errno,
                                         _("Failed to create controller %s for group"),
                                         virCgroupControllerTypeToString(i));
1044
                    return -1;
1045
                }
1046
            }
1047 1048
            if (group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint != NULL &&
                (i == VIR_CGROUP_CONTROLLER_CPUSET ||
E
Eric Blake 已提交
1049 1050
                 STREQ(group->controllers[i].mountPoint,
                       group->controllers[VIR_CGROUP_CONTROLLER_CPUSET].mountPoint))) {
1051 1052
                if (virCgroupCpuSetInherit(parent, group) < 0)
                    return -1;
1053
            }
1054 1055 1056 1057
            /*
             * Note that virCgroupSetMemoryUseHierarchy should always be
             * called prior to creating subcgroups and attaching tasks.
             */
1058 1059
            if ((flags & VIR_CGROUP_MEM_HIERACHY) &&
                (group->controllers[VIR_CGROUP_CONTROLLER_MEMORY].mountPoint != NULL) &&
1060
                (i == VIR_CGROUP_CONTROLLER_MEMORY ||
E
Eric Blake 已提交
1061 1062
                 STREQ(group->controllers[i].mountPoint,
                       group->controllers[VIR_CGROUP_CONTROLLER_MEMORY].mountPoint))) {
1063 1064
                if (virCgroupSetMemoryUseHierarchy(group) < 0)
                    return -1;
1065
            }
1066 1067 1068
        }
    }

1069
    VIR_DEBUG("Done making controllers for group");
1070
    return 0;
1071 1072
}

1073

1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
/**
 * 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.
 *
1088
 * Returns 0 on success, -1 on error
1089
 */
E
Eric Blake 已提交
1090 1091 1092 1093 1094 1095
static int
virCgroupNew(pid_t pid,
             const char *path,
             virCgroupPtr parent,
             int controllers,
             virCgroupPtr *group)
1096
{
1097 1098
    VIR_DEBUG("pid=%lld path=%s parent=%p controllers=%d group=%p",
              (long long) pid, path, parent, controllers, group);
1099
    *group = NULL;
1100

1101 1102
    if (VIR_ALLOC((*group)) < 0)
        goto error;
1103

1104
    if (path[0] == '/' || !parent) {
1105 1106
        if (VIR_STRDUP((*group)->path, path) < 0)
            goto error;
1107 1108 1109 1110
    } else {
        if (virAsprintf(&(*group)->path, "%s%s%s",
                        parent->path,
                        STREQ(parent->path, "") ? "" : "/",
1111 1112
                        path) < 0)
            goto error;
1113 1114
    }

1115
    if (virCgroupDetect(*group, pid, controllers, path, parent) < 0)
1116
        goto error;
1117

1118 1119
    return 0;

1120
 error:
1121
    virCgroupFree(group);
1122
    *group = NULL;
1123

1124
    return -1;
1125
}
1126

1127

1128 1129
static int
virCgroupAddTaskInternal(virCgroupPtr group, pid_t pid, bool withSystemd)
1130
{
1131
    int ret = -1;
1132
    size_t i;
1133

1134
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
1135 1136 1137
        /* Skip over controllers not mounted */
        if (!group->controllers[i].mountPoint)
            continue;
1138

1139 1140 1141 1142
        /* We must never add tasks in systemd's hierarchy
         * unless we're intentionally trying to move a
         * task into a systemd machine scope */
        if (i == VIR_CGROUP_CONTROLLER_SYSTEMD && !withSystemd)
1143 1144
            continue;

1145
        if (virCgroupAddTaskController(group, pid, i) < 0)
1146
            goto cleanup;
1147 1148
    }

1149
    ret = 0;
1150
 cleanup:
1151
    return ret;
1152 1153
}

1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
/**
 * virCgroupAddTask:
 *
 * @group: The cgroup to add a task to
 * @pid: The pid of the task to add
 *
 * Will add the task to all controllers, except the
 * systemd unit controller.
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupAddTask(virCgroupPtr group, pid_t pid)
{
    return virCgroupAddTaskInternal(group, pid, false);
}

/**
 * virCgroupAddMachineTask:
 *
 * @group: The cgroup to add a task to
 * @pid: The pid of the task to add
 *
 * Will add the task to all controllers, including the
 * systemd unit controller.
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupAddMachineTask(virCgroupPtr group, pid_t pid)
{
    return virCgroupAddTaskInternal(group, pid, true);
}

E
Eric Blake 已提交
1188

1189 1190 1191 1192 1193 1194 1195
/**
 * 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
 *
1196
 * Returns: 0 on success or -1 on error
1197
 */
E
Eric Blake 已提交
1198 1199
int
virCgroupAddTaskController(virCgroupPtr group, pid_t pid, int controller)
1200
{
1201 1202 1203 1204 1205
    if (controller < 0 || controller >= VIR_CGROUP_CONTROLLER_LAST) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Controller %d out of range"), controller);
        return -1;
    }
1206

1207 1208 1209 1210 1211 1212
    if (!group->controllers[controller].mountPoint) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Controller '%s' not mounted"),
                       virCgroupControllerTypeToString(controller));
        return -1;
    }
1213

M
Michal Privoznik 已提交
1214
    return virCgroupSetValueI64(group, controller, "tasks", pid);
1215 1216 1217
}


E
Eric Blake 已提交
1218 1219
static int
virCgroupSetPartitionSuffix(const char *path, char **res)
1220
{
1221
    char **tokens;
1222
    size_t i;
1223
    int ret = -1;
1224

1225
    if (!(tokens = virStringSplit(path, "/", 0)))
1226
        return ret;
1227

1228
    for (i = 0; tokens[i] != NULL; i++) {
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
        /* 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],
1244
                              strlen(tokens[i]) + strlen(".partition") + 1) < 0)
1245
                goto cleanup;
1246 1247
            strcat(tokens[i], ".partition");
        }
1248

1249
        if (virCgroupPartitionEscape(&(tokens[i])) < 0)
1250
            goto cleanup;
1251 1252
    }

1253
    if (!(*res = virStringListJoin((const char **)tokens, "/")))
1254
        goto cleanup;
1255

1256 1257 1258 1259 1260
    ret = 0;

 cleanup:
    virStringListFree(tokens);
    return ret;
1261 1262
}

E
Eric Blake 已提交
1263

1264 1265 1266 1267 1268 1269 1270
/**
 * 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
1271
 * partition path identified by @path.
1272
 *
1273
 * Returns 0 on success, -1 on failure
1274
 */
E
Eric Blake 已提交
1275 1276 1277 1278 1279
int
virCgroupNewPartition(const char *path,
                      bool create,
                      int controllers,
                      virCgroupPtr *group)
1280
{
1281
    int ret = -1;
1282 1283
    VIR_AUTOFREE(char *) parentPath = NULL;
    VIR_AUTOFREE(char *) newPath = NULL;
1284
    virCgroupPtr parent = NULL;
1285 1286 1287
    VIR_DEBUG("path=%s create=%d controllers=%x",
              path, create, controllers);

1288 1289 1290 1291 1292 1293
    if (path[0] != '/') {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Partition path '%s' must start with '/'"),
                       path);
        return -1;
    }
1294

1295
    if (virCgroupSetPartitionSuffix(path, &newPath) < 0)
1296
        goto cleanup;
1297

1298 1299
    if (virCgroupNew(-1, newPath, NULL, controllers, group) < 0)
        goto cleanup;
1300

1301
    if (STRNEQ(newPath, "/")) {
1302
        char *tmp;
1303
        if (VIR_STRDUP(parentPath, newPath) < 0)
1304
            goto cleanup;
1305 1306 1307 1308 1309

        tmp = strrchr(parentPath, '/');
        tmp++;
        *tmp = '\0';

1310
        if (virCgroupNew(-1, parentPath, NULL, controllers, &parent) < 0)
1311
            goto cleanup;
1312

1313 1314 1315
        if (virCgroupMakeGroup(parent, *group, create, VIR_CGROUP_NONE) < 0) {
            virCgroupRemove(*group);
            goto cleanup;
1316 1317 1318
        }
    }

1319 1320 1321
    ret = 0;
 cleanup:
    if (ret != 0)
1322 1323
        virCgroupFree(group);
    virCgroupFree(&parent);
1324
    return ret;
1325 1326
}

1327

G
Gao feng 已提交
1328
/**
1329
* virCgroupNewSelf:
G
Gao feng 已提交
1330 1331 1332
*
* @group: Pointer to returned virCgroupPtr
*
1333 1334 1335
* Obtain a cgroup representing the config of the
* current process
*
1336
* Returns 0 on success, or -1 on error
G
Gao feng 已提交
1337
*/
E
Eric Blake 已提交
1338 1339
int
virCgroupNewSelf(virCgroupPtr *group)
G
Gao feng 已提交
1340
{
1341
    return virCgroupNewDetect(-1, -1, group);
G
Gao feng 已提交
1342
}
1343

1344

1345 1346 1347 1348 1349 1350 1351 1352
/**
 * virCgroupNewDomainPartition:
 *
 * @partition: partition holding the domain
 * @driver: name of the driver
 * @name: name of the domain
 * @group: Pointer to returned virCgroupPtr
 *
1353
 * Returns 0 on success, or -1 on error
1354
 */
E
Eric Blake 已提交
1355 1356 1357 1358 1359 1360
int
virCgroupNewDomainPartition(virCgroupPtr partition,
                            const char *driver,
                            const char *name,
                            bool create,
                            virCgroupPtr *group)
1361
{
1362
    VIR_AUTOFREE(char *)grpname = NULL;
1363

1364
    if (virAsprintf(&grpname, "%s.libvirt-%s",
1365
                    name, driver) < 0)
1366
        return -1;
1367

1368
    if (virCgroupPartitionEscape(&grpname) < 0)
1369
        return -1;
1370

1371
    if (virCgroupNew(-1, grpname, partition, -1, group) < 0)
1372
        return -1;
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383

    /*
     * 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.
     */
E
Eric Blake 已提交
1384 1385
    if (virCgroupMakeGroup(partition, *group, create,
                           VIR_CGROUP_MEM_HIERACHY) < 0) {
1386
        virCgroupRemove(*group);
1387
        virCgroupFree(group);
1388
        return -1;
1389 1390
    }

1391
    return 0;
1392
}
1393

E
Eric Blake 已提交
1394

1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
/**
 * virCgroupNewThread:
 *
 * @domain: group for the domain
 * @name: enum to generate the name for the new thread
 * @id: id of the vcpu or iothread
 * @create: true to create if not already existing
 * @group: Pointer to returned virCgroupPtr
 *
 * Returns 0 on success, or -1 on error
 */
int
virCgroupNewThread(virCgroupPtr domain,
                   virCgroupThreadName nameval,
                   int id,
                   bool create,
                   virCgroupPtr *group)
{
1413
    VIR_AUTOFREE(char *) name = NULL;
1414 1415 1416 1417 1418
    int controllers;

    switch (nameval) {
    case VIR_CGROUP_THREAD_VCPU:
        if (virAsprintf(&name, "vcpu%d", id) < 0)
1419
            return -1;
1420 1421 1422
        break;
    case VIR_CGROUP_THREAD_EMULATOR:
        if (VIR_STRDUP(name, "emulator") < 0)
1423
            return -1;
1424 1425 1426
        break;
    case VIR_CGROUP_THREAD_IOTHREAD:
        if (virAsprintf(&name, "iothread%d", id) < 0)
1427
            return -1;
1428 1429 1430 1431
        break;
    case VIR_CGROUP_THREAD_LAST:
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected name value %d"), nameval);
1432
        return -1;
1433 1434 1435 1436 1437 1438 1439
    }

    controllers = ((1 << VIR_CGROUP_CONTROLLER_CPU) |
                   (1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
                   (1 << VIR_CGROUP_CONTROLLER_CPUSET));

    if (virCgroupNew(-1, name, domain, controllers, group) < 0)
1440
        return -1;
1441 1442 1443

    if (virCgroupMakeGroup(domain, *group, create, VIR_CGROUP_NONE) < 0) {
        virCgroupRemove(*group);
1444
        virCgroupFree(group);
1445
        return -1;
1446 1447
    }

1448
    return 0;
1449 1450 1451
}


E
Eric Blake 已提交
1452 1453 1454 1455
int
virCgroupNewDetect(pid_t pid,
                   int controllers,
                   virCgroupPtr *group)
1456
{
1457
    return virCgroupNew(pid, "", NULL, controllers, group);
1458 1459
}

E
Eric Blake 已提交
1460

1461 1462 1463
/*
 * Returns 0 on success (but @group may be NULL), -1 on fatal error
 */
E
Eric Blake 已提交
1464 1465 1466 1467 1468
int
virCgroupNewDetectMachine(const char *name,
                          const char *drivername,
                          pid_t pid,
                          int controllers,
1469
                          char *machinename,
E
Eric Blake 已提交
1470
                          virCgroupPtr *group)
1471
{
1472
    if (virCgroupNewDetect(pid, controllers, group) < 0) {
1473 1474 1475 1476 1477
        if (virCgroupNewIgnoreError())
            return 0;
        return -1;
    }

1478
    if (!virCgroupValidateMachineGroup(*group, name, drivername,
1479
                                       true, machinename)) {
1480 1481
        VIR_DEBUG("Failed to validate machine name for '%s' driver '%s'",
                  name, drivername);
1482
        virCgroupFree(group);
1483 1484 1485 1486 1487 1488
        return 0;
    }

    return 0;
}

E
Eric Blake 已提交
1489

1490 1491 1492 1493 1494 1495 1496 1497 1498 1499
/*
 * Returns 0 on success, -1 on fatal error, -2 on systemd not available
 */
static int
virCgroupNewMachineSystemd(const char *name,
                           const char *drivername,
                           const unsigned char *uuid,
                           const char *rootdir,
                           pid_t pidleader,
                           bool isContainer,
1500 1501
                           size_t nnicindexes,
                           int *nicindexes,
1502 1503 1504
                           const char *partition,
                           int controllers,
                           virCgroupPtr *group)
1505
{
1506
    int ret = -1;
1507
    int rv;
1508
    virCgroupPtr init, parent = NULL;
1509
    VIR_AUTOFREE(char *) path = NULL;
1510 1511 1512 1513 1514 1515 1516 1517 1518
    char *offset;

    VIR_DEBUG("Trying to setup machine '%s' via systemd", name);
    if ((rv = virSystemdCreateMachine(name,
                                      drivername,
                                      uuid,
                                      rootdir,
                                      pidleader,
                                      isContainer,
1519 1520
                                      nnicindexes,
                                      nicindexes,
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531
                                      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;
1532

1533 1534
    path = init->controllers[VIR_CGROUP_CONTROLLER_SYSTEMD].placement;
    init->controllers[VIR_CGROUP_CONTROLLER_SYSTEMD].placement = NULL;
1535
    virCgroupFree(&init);
1536 1537 1538

    if (!path || STREQ(path, "/") || path[0] != '/') {
        VIR_DEBUG("Systemd didn't setup its controller");
1539 1540
        ret = -2;
        goto cleanup;
1541 1542 1543 1544 1545 1546 1547 1548 1549
    }

    offset = path;

    if (virCgroupNew(pidleader,
                     "",
                     NULL,
                     controllers,
                     &parent) < 0)
1550
        goto cleanup;
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563


    for (;;) {
        virCgroupPtr tmp;
        char *t = strchr(offset + 1, '/');
        if (t)
            *t = '\0';

        if (virCgroupNew(pidleader,
                         path,
                         parent,
                         controllers,
                         &tmp) < 0)
1564
            goto cleanup;
1565 1566

        if (virCgroupMakeGroup(parent, tmp, true, VIR_CGROUP_NONE) < 0) {
1567
            virCgroupFree(&tmp);
1568
            goto cleanup;
1569 1570 1571 1572
        }
        if (t) {
            *t = '/';
            offset = t;
1573
            virCgroupFree(&parent);
1574 1575 1576 1577 1578 1579 1580
            parent = tmp;
        } else {
            *group = tmp;
            break;
        }
    }

1581 1582 1583
    if (virCgroupAddTask(*group, pidleader) < 0) {
        virErrorPtr saved = virSaveLastError();
        virCgroupRemove(*group);
1584
        virCgroupFree(group);
1585 1586 1587 1588 1589 1590
        if (saved) {
            virSetError(saved);
            virFreeError(saved);
        }
    }

1591 1592
    ret = 0;
 cleanup:
1593
    virCgroupFree(&parent);
1594
    return ret;
1595
}
1596

E
Eric Blake 已提交
1597

1598 1599 1600
/*
 * Returns 0 on success, -1 on fatal error
 */
1601
int virCgroupTerminateMachine(const char *name)
1602
{
1603
    return virSystemdTerminateMachine(name);
1604 1605 1606
}


1607 1608 1609
static int
virCgroupNewMachineManual(const char *name,
                          const char *drivername,
1610
                          pid_t pidleader,
1611 1612 1613 1614
                          const char *partition,
                          int controllers,
                          virCgroupPtr *group)
{
1615 1616
    virCgroupPtr parent = NULL;
    int ret = -1;
1617 1618

    VIR_DEBUG("Fallback to non-systemd setup");
1619 1620 1621 1622 1623
    if (virCgroupNewPartition(partition,
                              STREQ(partition, "/machine"),
                              controllers,
                              &parent) < 0) {
        if (virCgroupNewIgnoreError())
1624
            goto done;
1625

1626
        goto cleanup;
1627 1628 1629 1630 1631 1632 1633
    }

    if (virCgroupNewDomainPartition(parent,
                                    drivername,
                                    name,
                                    true,
                                    group) < 0)
1634
        goto cleanup;
1635

1636 1637 1638
    if (virCgroupAddTask(*group, pidleader) < 0) {
        virErrorPtr saved = virSaveLastError();
        virCgroupRemove(*group);
1639
        virCgroupFree(group);
1640 1641 1642 1643 1644 1645
        if (saved) {
            virSetError(saved);
            virFreeError(saved);
        }
    }

1646 1647 1648 1649
 done:
    ret = 0;

 cleanup:
1650
    virCgroupFree(&parent);
1651
    return ret;
1652 1653
}

E
Eric Blake 已提交
1654 1655 1656 1657 1658 1659 1660 1661

int
virCgroupNewMachine(const char *name,
                    const char *drivername,
                    const unsigned char *uuid,
                    const char *rootdir,
                    pid_t pidleader,
                    bool isContainer,
1662 1663
                    size_t nnicindexes,
                    int *nicindexes,
E
Eric Blake 已提交
1664 1665 1666
                    const char *partition,
                    int controllers,
                    virCgroupPtr *group)
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
{
    int rv;

    *group = NULL;

    if ((rv = virCgroupNewMachineSystemd(name,
                                         drivername,
                                         uuid,
                                         rootdir,
                                         pidleader,
                                         isContainer,
1678 1679
                                         nnicindexes,
                                         nicindexes,
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
                                         partition,
                                         controllers,
                                         group)) == 0)
        return 0;

    if (rv == -1)
        return -1;

    return virCgroupNewMachineManual(name,
                                     drivername,
1690
                                     pidleader,
1691 1692 1693 1694 1695
                                     partition,
                                     controllers,
                                     group);
}

E
Eric Blake 已提交
1696 1697 1698

bool
virCgroupNewIgnoreError(void)
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
{
    if (virLastErrorIsSystemErrno(ENXIO) ||
        virLastErrorIsSystemErrno(EPERM) ||
        virLastErrorIsSystemErrno(EACCES)) {
        virResetLastError();
        VIR_DEBUG("No cgroups present/configured/accessible, ignoring error");
        return true;
    }
    return false;
}

E
Eric Blake 已提交
1710

E
Eric Blake 已提交
1711 1712 1713 1714 1715 1716
/**
 * virCgroupFree:
 *
 * @group: The group structure to free
 */
void
1717
virCgroupFree(virCgroupPtr *group)
E
Eric Blake 已提交
1718 1719 1720
{
    size_t i;

1721
    if (*group == NULL)
E
Eric Blake 已提交
1722 1723 1724
        return;

    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
1725 1726 1727
        VIR_FREE((*group)->controllers[i].mountPoint);
        VIR_FREE((*group)->controllers[i].linkPoint);
        VIR_FREE((*group)->controllers[i].placement);
E
Eric Blake 已提交
1728 1729
    }

1730 1731
    VIR_FREE((*group)->path);
    VIR_FREE(*group);
E
Eric Blake 已提交
1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805
}


/**
 * 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;
}


1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
/**
 * 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;
1825 1826 1827 1828
    VIR_AUTOFREE(char *) str1 = NULL;
    VIR_AUTOFREE(char *) str2 = NULL;
    char *p1 = NULL;
    char *p2 = NULL;
1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851
    size_t i;

    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)
1852
        return -1;
1853 1854 1855 1856

    if (virCgroupGetValueStr(group,
                             VIR_CGROUP_CONTROLLER_BLKIO,
                             "blkio.throttle.io_serviced", &str2) < 0)
1857
        return -1;
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870

    /* 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);
1871
                return -1;
1872 1873 1874 1875 1876 1877 1878 1879
            }

            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]);
1880
                return -1;
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891
            }
            *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);
1892
                return -1;
1893 1894 1895 1896 1897 1898 1899 1900
            }

            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]);
1901
                return -1;
1902 1903 1904 1905 1906
            }
            *requests_ptrs[i] += stats_val;
        }
    }

1907
    return 0;
1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
}


/**
 * 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)
{
1931 1932 1933 1934 1935
    VIR_AUTOFREE(char *) str1 = NULL;
    VIR_AUTOFREE(char *) str2 = NULL;
    VIR_AUTOFREE(char *) str3 = NULL;
    char *p1 = NULL;
    char *p2 = NULL;
1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953
    size_t i;

    const char *value_names[] = {
        "Read ",
        "Write "
    };
    long long *bytes_ptrs[] = {
        bytes_read,
        bytes_write
    };
    long long *requests_ptrs[] = {
        requests_read,
        requests_write
    };

    if (virCgroupGetValueStr(group,
                             VIR_CGROUP_CONTROLLER_BLKIO,
                             "blkio.throttle.io_service_bytes", &str1) < 0)
1954
        return -1;
1955 1956 1957 1958

    if (virCgroupGetValueStr(group,
                             VIR_CGROUP_CONTROLLER_BLKIO,
                             "blkio.throttle.io_serviced", &str2) < 0)
1959
        return -1;
1960

1961
    if (!(str3 = virCgroupGetBlockDevString(path)))
1962
        return -1;
1963 1964 1965 1966 1967

    if (!(p1 = strstr(str1, str3))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Cannot find byte stats for block device '%s'"),
                       str3);
1968
        return -1;
1969 1970 1971 1972 1973 1974
    }

    if (!(p2 = strstr(str2, str3))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Cannot find request stats for block device '%s'"),
                       str3);
1975
        return -1;
1976 1977 1978 1979 1980 1981 1982
    }

    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);
1983
            return -1;
1984 1985 1986 1987 1988 1989
        }

        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]));
1990
            return -1;
1991 1992 1993 1994 1995 1996
        }

        if (!(p2 = strstr(p2, value_names[i]))) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Cannot find request %sstats for block device '%s'"),
                           value_names[i], str3);
1997
            return -1;
1998 1999 2000 2001 2002 2003
        }

        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]));
2004
            return -1;
2005 2006 2007
        }
    }

2008
    return 0;
2009 2010 2011
}


2012 2013 2014 2015 2016 2017
/**
 * virCgroupSetBlkioWeight:
 *
 * @group: The cgroup to change io weight for
 * @weight: The Weight for this cgroup
 *
2018
 * Returns: 0 on success, -1 on error
2019
 */
E
Eric Blake 已提交
2020 2021
int
virCgroupSetBlkioWeight(virCgroupPtr group, unsigned int weight)
2022 2023 2024 2025 2026 2027 2028
{
    return virCgroupSetValueU64(group,
                                VIR_CGROUP_CONTROLLER_BLKIO,
                                "blkio.weight",
                                weight);
}

E
Eric Blake 已提交
2029

2030 2031 2032 2033 2034 2035
/**
 * virCgroupGetBlkioWeight:
 *
 * @group: The cgroup to get weight for
 * @Weight: Pointer to returned weight
 *
2036
 * Returns: 0 on success, -1 on error
2037
 */
E
Eric Blake 已提交
2038 2039
int
virCgroupGetBlkioWeight(virCgroupPtr group, unsigned int *weight)
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050
{
    unsigned long long tmp;
    int ret;
    ret = virCgroupGetValueU64(group,
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.weight", &tmp);
    if (ret == 0)
        *weight = tmp;
    return ret;
}

2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
/**
 * 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)
{
2064 2065
    VIR_AUTOFREE(char *) str = NULL;
    VIR_AUTOFREE(char *) blkstr = NULL;
2066

2067
    if (!(blkstr = virCgroupGetBlockDevString(path)))
2068 2069
        return -1;

2070
    if (virAsprintf(&str, "%s%u", blkstr, riops) < 0)
2071
        return -1;
2072

2073
    return virCgroupSetValueStr(group,
2074 2075 2076 2077 2078
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.throttle.read_iops_device",
                               str);
}

E
Eric Blake 已提交
2079

2080
/**
2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
 * 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)
{
2093 2094
    VIR_AUTOFREE(char *) str = NULL;
    VIR_AUTOFREE(char *) blkstr = NULL;
2095

2096
    if (!(blkstr = virCgroupGetBlockDevString(path)))
2097 2098
        return -1;

2099
    if (virAsprintf(&str, "%s%u", blkstr, wiops) < 0)
2100
        return -1;
2101

2102
    return virCgroupSetValueStr(group,
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.throttle.write_iops_device",
                               str);
}


/**
 * 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)
{
2122 2123
    VIR_AUTOFREE(char *) str = NULL;
    VIR_AUTOFREE(char *) blkstr = NULL;
2124

2125
    if (!(blkstr = virCgroupGetBlockDevString(path)))
2126 2127
        return -1;

2128
    if (virAsprintf(&str, "%s%llu", blkstr, rbps) < 0)
2129
        return -1;
2130

2131
    return virCgroupSetValueStr(group,
2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.throttle.read_bps_device",
                               str);
}

/**
 * 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
2142
 *
2143 2144 2145 2146 2147 2148 2149
 * Returns: 0 on success, -1 on error
 */
int
virCgroupSetBlkioDeviceWriteBps(virCgroupPtr group,
                                const char *path,
                                unsigned long long wbps)
{
2150 2151
    VIR_AUTOFREE(char *) str = NULL;
    VIR_AUTOFREE(char *) blkstr = NULL;
2152

2153
    if (!(blkstr = virCgroupGetBlockDevString(path)))
2154 2155
        return -1;

2156
    if (virAsprintf(&str, "%s%llu", blkstr, wbps) < 0)
2157
        return -1;
2158

2159
    return virCgroupSetValueStr(group,
2160 2161 2162 2163 2164 2165 2166 2167 2168 2169
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.throttle.write_bps_device",
                               str);
}


/**
 * virCgroupSetBlkioDeviceWeight:
 * @group: The cgroup to change block io setting for
 * @path: The path of device
2170 2171
 * @weight: The new device weight (100-1000),
 * (10-1000) after kernel 2.6.39, or 0 to clear
2172
 *
2173
 * Returns: 0 on success, -1 on error
2174
 */
E
Eric Blake 已提交
2175 2176 2177 2178
int
virCgroupSetBlkioDeviceWeight(virCgroupPtr group,
                              const char *path,
                              unsigned int weight)
2179
{
2180 2181
    VIR_AUTOFREE(char *) str = NULL;
    VIR_AUTOFREE(char *) blkstr = NULL;
2182

2183
    if (!(blkstr = virCgroupGetBlockDevString(path)))
2184
        return -1;
2185

2186
    if (virAsprintf(&str, "%s%d", blkstr, weight) < 0)
2187
        return -1;
2188

2189
    return virCgroupSetValueStr(group,
2190 2191 2192 2193
                               VIR_CGROUP_CONTROLLER_BLKIO,
                               "blkio.weight_device",
                               str);
}
2194

2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207
/**
 * virCgroupGetBlkioDeviceReadIops:
 * @group: The cgroup to gather block io setting for
 * @path: The path of device
 * @riops: Returned device read iops throttle, 0 if there is none
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupGetBlkioDeviceReadIops(virCgroupPtr group,
                                const char *path,
                                unsigned int *riops)
{
2208
    VIR_AUTOFREE(char *) str = NULL;
2209 2210 2211 2212 2213 2214

    if (virCgroupGetValueForBlkDev(group,
                                   VIR_CGROUP_CONTROLLER_BLKIO,
                                   "blkio.throttle.read_iops_device",
                                   path,
                                   &str) < 0)
2215
        return -1;
2216 2217 2218 2219 2220 2221 2222

    if (!str) {
        *riops = 0;
    } else if (virStrToLong_ui(str, NULL, 10, riops) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       str);
2223
        return -1;
2224 2225
    }

2226
    return 0;
2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
}

/**
 * virCgroupGetBlkioDeviceWriteIops:
 * @group: The cgroup to gather block io setting for
 * @path: The path of device
 * @wiops: Returned device write iops throttle, 0 if there is none
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupGetBlkioDeviceWriteIops(virCgroupPtr group,
                                 const char *path,
                                 unsigned int *wiops)
{
2242
    VIR_AUTOFREE(char *) str = NULL;
2243 2244 2245 2246 2247 2248

    if (virCgroupGetValueForBlkDev(group,
                                   VIR_CGROUP_CONTROLLER_BLKIO,
                                   "blkio.throttle.write_iops_device",
                                   path,
                                   &str) < 0)
2249
        return -1;
2250 2251 2252 2253 2254 2255 2256

    if (!str) {
        *wiops = 0;
    } else if (virStrToLong_ui(str, NULL, 10, wiops) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       str);
2257
        return -1;
2258 2259
    }

2260
    return 0;
2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275
}

/**
 * virCgroupGetBlkioDeviceReadBps:
 * @group: The cgroup to gather block io setting for
 * @path: The path of device
 * @rbps: Returned device read bps throttle, 0 if there is none
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupGetBlkioDeviceReadBps(virCgroupPtr group,
                               const char *path,
                               unsigned long long *rbps)
{
2276
    VIR_AUTOFREE(char *) str = NULL;
2277 2278 2279 2280 2281 2282

    if (virCgroupGetValueForBlkDev(group,
                                   VIR_CGROUP_CONTROLLER_BLKIO,
                                   "blkio.throttle.read_bps_device",
                                   path,
                                   &str) < 0)
2283
        return -1;
2284 2285 2286 2287 2288 2289 2290

    if (!str) {
        *rbps = 0;
    } else if (virStrToLong_ull(str, NULL, 10, rbps) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       str);
2291
        return -1;
2292 2293
    }

2294
    return 0;
2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309
}

/**
 * virCgroupGetBlkioDeviceWriteBps:
 * @group: The cgroup to gather block io setting for
 * @path: The path of device
 * @wbps: Returned device write bps throttle, 0 if there is none
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupGetBlkioDeviceWriteBps(virCgroupPtr group,
                                const char *path,
                                unsigned long long *wbps)
{
2310
    VIR_AUTOFREE(char *) str = NULL;
2311 2312 2313 2314 2315 2316

    if (virCgroupGetValueForBlkDev(group,
                                   VIR_CGROUP_CONTROLLER_BLKIO,
                                   "blkio.throttle.write_bps_device",
                                   path,
                                   &str) < 0)
2317
        return -1;
2318 2319 2320 2321 2322 2323 2324

    if (!str) {
        *wbps = 0;
    } else if (virStrToLong_ull(str, NULL, 10, wbps) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       str);
2325
        return -1;
2326 2327
    }

2328
    return 0;
2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
}

/**
 * virCgroupGetBlkioDeviceWeight:
 * @group: The cgroup to gather block io setting for
 * @path: The path of device
 * @weight: Returned device weight, 0 if there is none
 *
 * Returns: 0 on success, -1 on error
 */
int
virCgroupGetBlkioDeviceWeight(virCgroupPtr group,
                              const char *path,
                              unsigned int *weight)
{
2344
    VIR_AUTOFREE(char *) str = NULL;
2345 2346 2347 2348 2349 2350

    if (virCgroupGetValueForBlkDev(group,
                                   VIR_CGROUP_CONTROLLER_BLKIO,
                                   "blkio.weight_device",
                                   path,
                                   &str) < 0)
2351
        return -1;
2352 2353 2354 2355 2356 2357 2358

    if (!str) {
        *weight = 0;
    } else if (virStrToLong_ui(str, NULL, 10, weight) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse '%s' as an integer"),
                       str);
2359
        return -1;
2360 2361
    }

2362
    return 0;
2363 2364
}

2365

2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379
/*
 * Retrieve the "memory.limit_in_bytes" value from the memory controller
 * root dir. This value cannot be modified by userspace and therefore
 * is the maximum limit value supported by cgroups on the local system.
 * Returns this value scaled to KB or falls back to the original
 * VIR_DOMAIN_MEMORY_PARAM_UNLIMITED. Either way, remember the return
 * value to avoid unnecessary cgroup filesystem access.
 */
static unsigned long long int virCgroupMemoryUnlimitedKB;
static virOnceControl virCgroupMemoryOnce = VIR_ONCE_CONTROL_INITIALIZER;

static void
virCgroupMemoryOnceInit(void)
{
2380
    virCgroupPtr group;
2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393
    unsigned long long int mem_unlimited = 0ULL;

    if (virCgroupNew(-1, "/", NULL, -1, &group) < 0)
        goto cleanup;

    if (!virCgroupHasController(group, VIR_CGROUP_CONTROLLER_MEMORY))
        goto cleanup;

    ignore_value(virCgroupGetValueU64(group,
                                      VIR_CGROUP_CONTROLLER_MEMORY,
                                      "memory.limit_in_bytes",
                                      &mem_unlimited));
 cleanup:
2394
    virCgroupFree(&group);
2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410
    virCgroupMemoryUnlimitedKB = mem_unlimited >> 10;
}

static unsigned long long int
virCgroupGetMemoryUnlimitedKB(void)
{
    if (virOnce(&virCgroupMemoryOnce, virCgroupMemoryOnceInit) < 0)
        VIR_DEBUG("Init failed, will fall back to defaults.");

    if (virCgroupMemoryUnlimitedKB)
        return virCgroupMemoryUnlimitedKB;
    else
        return VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
}


2411 2412 2413 2414 2415 2416 2417 2418
/**
 * virCgroupSetMemory:
 *
 * @group: The cgroup to change memory for
 * @kb: The memory amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2419 2420
int
virCgroupSetMemory(virCgroupPtr group, unsigned long long kb)
2421
{
2422 2423
    unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2424 2425 2426 2427 2428 2429 2430 2431
    if (kb > maxkb) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("Memory '%llu' must be less than %llu"),
                       kb, maxkb);
        return -1;
    }

    if (kb == maxkb)
2432 2433 2434 2435 2436 2437 2438 2439 2440
        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);
2441 2442
}

E
Eric Blake 已提交
2443

R
Ryota Ozaki 已提交
2444 2445 2446 2447 2448 2449 2450 2451
/**
 * virCgroupGetMemoryUsage:
 *
 * @group: The cgroup to change memory for
 * @kb: Pointer to returned used memory in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2452 2453
int
virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
R
Ryota Ozaki 已提交
2454
{
C
Cole Robinson 已提交
2455
    long long unsigned int usage_in_bytes;
R
Ryota Ozaki 已提交
2456 2457 2458 2459 2460 2461 2462 2463 2464
    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;
}

E
Eric Blake 已提交
2465

2466 2467 2468 2469 2470 2471 2472 2473
/**
 * virCgroupSetMemoryHardLimit:
 *
 * @group: The cgroup to change memory hard limit for
 * @kb: The memory amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2474 2475
int
virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
2476 2477 2478 2479
{
    return virCgroupSetMemory(group, kb);
}

E
Eric Blake 已提交
2480

2481 2482 2483 2484 2485 2486 2487 2488
/**
 * virCgroupGetMemoryHardLimit:
 *
 * @group: The cgroup to get the memory hard limit for
 * @kb: The memory amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2489 2490
int
virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
2491 2492
{
    long long unsigned int limit_in_bytes;
2493 2494 2495 2496

    if (virCgroupGetValueU64(group,
                             VIR_CGROUP_CONTROLLER_MEMORY,
                             "memory.limit_in_bytes", &limit_in_bytes) < 0)
2497
        return -1;
2498 2499

    *kb = limit_in_bytes >> 10;
2500
    if (*kb >= virCgroupGetMemoryUnlimitedKB())
2501 2502
        *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2503
    return 0;
2504 2505
}

E
Eric Blake 已提交
2506

2507 2508 2509 2510 2511 2512 2513 2514
/**
 * virCgroupSetMemorySoftLimit:
 *
 * @group: The cgroup to change memory soft limit for
 * @kb: The memory amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2515 2516
int
virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
2517
{
2518 2519
    unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2520 2521 2522 2523 2524 2525 2526 2527
    if (kb > maxkb) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("Memory '%llu' must be less than %llu"),
                       kb, maxkb);
        return -1;
    }

    if (kb == maxkb)
2528 2529 2530 2531 2532 2533 2534 2535 2536
        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);
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547
}


/**
 * virCgroupGetMemorySoftLimit:
 *
 * @group: The cgroup to get the memory soft limit for
 * @kb: The memory amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2548 2549
int
virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
2550 2551
{
    long long unsigned int limit_in_bytes;
2552 2553 2554 2555

    if (virCgroupGetValueU64(group,
                             VIR_CGROUP_CONTROLLER_MEMORY,
                             "memory.soft_limit_in_bytes", &limit_in_bytes) < 0)
2556
        return -1;
2557 2558

    *kb = limit_in_bytes >> 10;
2559
    if (*kb >= virCgroupGetMemoryUnlimitedKB())
2560 2561
        *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2562
    return 0;
2563 2564
}

E
Eric Blake 已提交
2565

2566
/**
2567
 * virCgroupSetMemSwapHardLimit:
2568
 *
2569 2570
 * @group: The cgroup to change mem+swap hard limit for
 * @kb: The mem+swap amount in kilobytes
2571 2572 2573
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2574 2575
int
virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
2576
{
2577 2578
    unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2579 2580 2581 2582 2583 2584 2585 2586
    if (kb > maxkb) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("Memory '%llu' must be less than %llu"),
                       kb, maxkb);
        return -1;
    }

    if (kb == maxkb)
2587 2588 2589 2590 2591 2592 2593 2594 2595
        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);
2596 2597
}

E
Eric Blake 已提交
2598

2599
/**
2600
 * virCgroupGetMemSwapHardLimit:
2601
 *
2602 2603
 * @group: The cgroup to get mem+swap hard limit for
 * @kb: The mem+swap amount in kilobytes
2604 2605 2606
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2607 2608
int
virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
2609 2610
{
    long long unsigned int limit_in_bytes;
2611 2612 2613 2614

    if (virCgroupGetValueU64(group,
                             VIR_CGROUP_CONTROLLER_MEMORY,
                             "memory.memsw.limit_in_bytes", &limit_in_bytes) < 0)
2615
        return -1;
2616 2617

    *kb = limit_in_bytes >> 10;
2618
    if (*kb >= virCgroupGetMemoryUnlimitedKB())
2619 2620
        *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;

2621
    return 0;
2622 2623
}

E
Eric Blake 已提交
2624

G
Gao feng 已提交
2625 2626 2627 2628 2629 2630 2631 2632
/**
 * virCgroupGetMemSwapUsage:
 *
 * @group: The cgroup to get mem+swap usage for
 * @kb: The mem+swap amount in kilobytes
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2633 2634
int
virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
G
Gao feng 已提交
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645
{
    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;
}

E
Eric Blake 已提交
2646

2647 2648 2649 2650 2651 2652 2653 2654
/**
 * virCgroupSetCpusetMems:
 *
 * @group: The cgroup to set cpuset.mems for
 * @mems: the numa nodes to set
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2655 2656
int
virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
2657 2658 2659 2660 2661 2662 2663
{
    return virCgroupSetValueStr(group,
                                VIR_CGROUP_CONTROLLER_CPUSET,
                                "cpuset.mems",
                                mems);
}

E
Eric Blake 已提交
2664

2665 2666 2667 2668 2669 2670 2671 2672
/**
 * virCgroupGetCpusetMems:
 *
 * @group: The cgroup to get cpuset.mems for
 * @mems: the numa nodes to get
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2673 2674
int
virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
2675 2676 2677 2678 2679 2680 2681
{
    return virCgroupGetValueStr(group,
                                VIR_CGROUP_CONTROLLER_CPUSET,
                                "cpuset.mems",
                                mems);
}

E
Eric Blake 已提交
2682

2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721
/**
 * virCgroupSetCpusetMemoryMigrate:
 *
 * @group: The cgroup to set cpuset.memory_migrate for
 * @migrate: Whether to migrate the memory on change or not
 *
 * Returns: 0 on success
 */
int
virCgroupSetCpusetMemoryMigrate(virCgroupPtr group, bool migrate)
{
    return virCgroupSetValueStr(group,
                                VIR_CGROUP_CONTROLLER_CPUSET,
                                "cpuset.memory_migrate",
                                migrate ? "1" : "0");
}


/**
 * virCgroupGetCpusetMemoryMigrate:
 *
 * @group: The cgroup to get cpuset.memory_migrate for
 * @migrate: Migration setting
 *
 * Returns: 0 on success
 */
int
virCgroupGetCpusetMemoryMigrate(virCgroupPtr group, bool *migrate)
{
    unsigned long long value = 0;
    int ret = virCgroupGetValueU64(group,
                                   VIR_CGROUP_CONTROLLER_CPUSET,
                                   "cpuset.memory_migrate",
                                   &value);
    *migrate = !!value;
    return ret;
}


2722 2723 2724 2725 2726 2727
/**
 * virCgroupSetCpusetCpus:
 *
 * @group: The cgroup to set cpuset.cpus for
 * @cpus: the cpus to set
 *
N
Nitesh Konkar 已提交
2728
 * Returns: 0 on success
2729
 */
E
Eric Blake 已提交
2730 2731
int
virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
2732 2733 2734 2735 2736 2737 2738
{
    return virCgroupSetValueStr(group,
                                VIR_CGROUP_CONTROLLER_CPUSET,
                                "cpuset.cpus",
                                cpus);
}

E
Eric Blake 已提交
2739

2740 2741 2742 2743 2744 2745
/**
 * virCgroupGetCpusetCpus:
 *
 * @group: The cgroup to get cpuset.cpus for
 * @cpus: the cpus to get
 *
N
Nitesh Konkar 已提交
2746
 * Returns: 0 on success
2747
 */
E
Eric Blake 已提交
2748 2749
int
virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
2750 2751 2752 2753 2754 2755 2756
{
    return virCgroupGetValueStr(group,
                                VIR_CGROUP_CONTROLLER_CPUSET,
                                "cpuset.cpus",
                                cpus);
}

E
Eric Blake 已提交
2757

2758 2759 2760
/**
 * virCgroupDenyAllDevices:
 *
2761
 * @group: The cgroup to deny all permissions, for all devices
2762 2763 2764
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2765 2766
int
virCgroupDenyAllDevices(virCgroupPtr group)
2767 2768
{
    return virCgroupSetValueStr(group,
2769 2770 2771
                                VIR_CGROUP_CONTROLLER_DEVICES,
                                "devices.deny",
                                "a");
2772 2773
}

2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804
/**
 * 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;
}

E
Eric Blake 已提交
2805

2806 2807 2808 2809 2810
/**
 * virCgroupAllowDevice:
 *
 * @group: The cgroup to allow a device for
 * @type: The device type (i.e., 'c' or 'b')
2811 2812
 * @major: The major number of the device, a negative value means '*'
 * @minor: The minor number of the device, a negative value means '*'
2813
 * @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
2814 2815 2816
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2817 2818 2819
int
virCgroupAllowDevice(virCgroupPtr group, char type, int major, int minor,
                     int perms)
2820
{
2821 2822 2823
    VIR_AUTOFREE(char *) devstr = NULL;
    VIR_AUTOFREE(char *) majorstr = NULL;
    VIR_AUTOFREE(char *) minorstr = NULL;
2824

2825
    if ((major < 0 && VIR_STRDUP(majorstr, "*") < 0) ||
2826
        (major >= 0 && virAsprintf(&majorstr, "%i", major) < 0))
2827
        return -1;
2828 2829

    if ((minor < 0 && VIR_STRDUP(minorstr, "*") < 0) ||
2830
        (minor >= 0 && virAsprintf(&minorstr, "%i", minor) < 0))
2831
        return -1;
2832 2833

    if (virAsprintf(&devstr, "%c %s:%s %s", type, majorstr, minorstr,
2834
                    virCgroupGetDevicePermsString(perms)) < 0)
2835
        return -1;
2836

2837 2838 2839 2840
    if (virCgroupSetValueStr(group,
                             VIR_CGROUP_CONTROLLER_DEVICES,
                             "devices.allow",
                             devstr) < 0)
2841
        return -1;
2842

2843
    return 0;
2844
}
2845

E
Eric Blake 已提交
2846

2847 2848 2849 2850 2851
/**
 * virCgroupAllowDevicePath:
 *
 * @group: The cgroup to allow the device for
 * @path: the device to allow
2852
 * @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
2853
 * @ignoreEacces: Ignore lack of permission (mostly for NFS mounts)
2854 2855 2856 2857
 *
 * Queries the type of device and its major/minor number, and
 * adds that to the cgroup ACL
 *
2858 2859
 * Returns: 0 on success, 1 if path exists but is not a device or is not
 * accesible, or * -1 on error
2860
 */
E
Eric Blake 已提交
2861
int
2862 2863 2864 2865
virCgroupAllowDevicePath(virCgroupPtr group,
                         const char *path,
                         int perms,
                         bool ignoreEacces)
2866 2867 2868
{
    struct stat sb;

2869
    if (stat(path, &sb) < 0) {
2870 2871 2872
        if (errno == EACCES && ignoreEacces)
            return 1;

2873 2874 2875 2876 2877
        virReportSystemError(errno,
                             _("Path '%s' is not accessible"),
                             path);
        return -1;
    }
2878 2879

    if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
2880
        return 1;
2881 2882 2883 2884

    return virCgroupAllowDevice(group,
                                S_ISCHR(sb.st_mode) ? 'c' : 'b',
                                major(sb.st_rdev),
2885 2886
                                minor(sb.st_rdev),
                                perms);
2887
}
D
Daniel P. Berrange 已提交
2888

2889 2890 2891 2892 2893 2894

/**
 * virCgroupDenyDevice:
 *
 * @group: The cgroup to deny a device for
 * @type: The device type (i.e., 'c' or 'b')
2895 2896
 * @major: The major number of the device, a negative value means '*'
 * @minor: The minor number of the device, a negative value means '*'
2897
 * @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to deny
2898 2899 2900
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
2901 2902 2903
int
virCgroupDenyDevice(virCgroupPtr group, char type, int major, int minor,
                    int perms)
2904
{
2905 2906 2907
    VIR_AUTOFREE(char *) devstr = NULL;
    VIR_AUTOFREE(char *) majorstr = NULL;
    VIR_AUTOFREE(char *) minorstr = NULL;
2908 2909 2910

    if ((major < 0 && VIR_STRDUP(majorstr, "*") < 0) ||
        (major >= 0 && virAsprintf(&majorstr, "%i", major) < 0))
2911
        return -1;
2912

2913 2914
    if ((minor < 0 && VIR_STRDUP(minorstr, "*") < 0) ||
        (minor >= 0 && virAsprintf(&minorstr, "%i", minor) < 0))
2915
        return -1;
2916 2917

    if (virAsprintf(&devstr, "%c %s:%s %s", type, majorstr, minorstr,
2918
                    virCgroupGetDevicePermsString(perms)) < 0)
2919
        return -1;
2920

2921 2922 2923 2924
    if (virCgroupSetValueStr(group,
                             VIR_CGROUP_CONTROLLER_DEVICES,
                             "devices.deny",
                             devstr) < 0)
2925
        return -1;
2926

2927
    return 0;
2928 2929
}

E
Eric Blake 已提交
2930

2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944
/**
 * virCgroupDenyDevicePath:
 *
 * @group: The cgroup to deny the device for
 * @path: the device to deny
 * @perms: Bitwise or of VIR_CGROUP_DEVICE permission bits to allow
 * @ignoreEacces: Ignore lack of permission (mostly for NFS mounts)
 *
 * Queries the type of device and its major/minor number, and
 * removes it from the cgroup ACL
 *
 * Returns: 0 on success, 1 if path exists but is not a device or is not
 * accessible, or -1 on error.
 */
E
Eric Blake 已提交
2945
int
2946 2947 2948 2949
virCgroupDenyDevicePath(virCgroupPtr group,
                        const char *path,
                        int perms,
                        bool ignoreEacces)
2950 2951 2952
{
    struct stat sb;

2953
    if (stat(path, &sb) < 0) {
2954 2955 2956
        if (errno == EACCES && ignoreEacces)
            return 1;

2957 2958 2959 2960 2961
        virReportSystemError(errno,
                             _("Path '%s' is not accessible"),
                             path);
        return -1;
    }
2962 2963

    if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
2964
        return 1;
2965 2966 2967 2968

    return virCgroupDenyDevice(group,
                               S_ISCHR(sb.st_mode) ? 'c' : 'b',
                               major(sb.st_rdev),
2969 2970
                               minor(sb.st_rdev),
                               perms);
2971 2972
}

E
Eric Blake 已提交
2973

2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989
/* 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,
2990
                          virBitmapPtr guestvcpus,
2991
                          unsigned long long *sum_cpu_time,
2992 2993
                          size_t nsum,
                          virBitmapPtr cpumap)
2994
{
2995
    int ret = -1;
2996
    ssize_t i = -1;
2997
    virCgroupPtr group_vcpu = NULL;
2998

2999
    while ((i = virBitmapNextSetBit(guestvcpus, i)) >= 0) {
3000
        VIR_AUTOFREE(char *) buf = NULL;
3001 3002
        char *pos;
        unsigned long long tmp;
3003
        ssize_t j;
3004

J
John Ferlan 已提交
3005 3006
        if (virCgroupNewThread(group, VIR_CGROUP_THREAD_VCPU, i,
                               false, &group_vcpu) < 0)
3007
            goto cleanup;
3008 3009

        if (virCgroupGetCpuacctPercpuUsage(group_vcpu, &buf) < 0)
3010
            goto cleanup;
3011 3012

        pos = buf;
3013 3014 3015
        for (j = virBitmapNextSetBit(cpumap, -1);
             j >= 0 && j < nsum;
             j = virBitmapNextSetBit(cpumap, j)) {
3016 3017 3018
            if (virStrToLong_ull(pos, &pos, 10, &tmp) < 0) {
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("cpuacct parse error"));
3019
                goto cleanup;
3020 3021 3022
            }
            sum_cpu_time[j] += tmp;
        }
3023

3024
        virCgroupFree(&group_vcpu);
3025 3026
    }

3027 3028
    ret = 0;
 cleanup:
3029
    virCgroupFree(&group_vcpu);
3030
    return ret;
3031 3032 3033
}


3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053
/**
 * virCgroupGetPercpuStats:
 * @cgroup: cgroup data structure
 * @params: typed parameter array where data is returned
 * @nparams: cardinality of @params
 * @start_cpu: offset of physical CPU to get data for
 * @ncpus: number of physical CPUs to get data for
 * @nvcpupids: number of vCPU threads for a domain (actual number of vcpus)
 *
 * This function is the worker that retrieves data in the appropriate format
 * for the terribly designed 'virDomainGetCPUStats' API. Sharing semantics with
 * the API, this function has two modes of operation depending on magic settings
 * of the input arguments. Please refer to docs of 'virDomainGetCPUStats' for
 * the usage patterns of the similarly named arguments.
 *
 * @nvcpupids determines the count of active vcpu threads for the vm. If the
 * threads could not be detected the percpu data is skipped.
 *
 * Please DON'T use this function anywhere else.
 */
3054 3055 3056 3057 3058
int
virCgroupGetPercpuStats(virCgroupPtr group,
                        virTypedParameterPtr params,
                        unsigned int nparams,
                        int start_cpu,
3059
                        unsigned int ncpus,
3060
                        virBitmapPtr guestvcpus)
3061
{
3062
    int ret = -1;
3063
    size_t i;
3064
    int need_cpus, total_cpus;
3065
    char *pos;
3066 3067
    VIR_AUTOFREE(char *) buf = NULL;
    VIR_AUTOFREE(unsigned long long *) sum_cpu_time = NULL;
3068 3069 3070
    virTypedParameterPtr ent;
    int param_idx;
    unsigned long long cpu_time;
3071
    virBitmapPtr cpumap = NULL;
3072 3073

    /* return the number of supported params */
3074
    if (nparams == 0 && ncpus != 0) {
3075
        if (!guestvcpus)
3076 3077 3078 3079
            return CGROUP_NB_PER_CPU_STAT_PARAM;
        else
            return CGROUP_NB_PER_CPU_STAT_PARAM + 1;
    }
3080 3081

    /* To parse account file, we need to know how many cpus are present.  */
3082
    if (!(cpumap = virHostCPUGetPresentBitmap()))
3083
        return -1;
3084

3085 3086
    total_cpus = virBitmapSize(cpumap);

3087
    /* return total number of cpus */
3088 3089 3090 3091
    if (ncpus == 0) {
        ret = total_cpus;
        goto cleanup;
    }
3092

3093
    if (start_cpu >= total_cpus) {
3094 3095
        virReportError(VIR_ERR_INVALID_ARG,
                       _("start_cpu %d larger than maximum of %d"),
3096
                       start_cpu, total_cpus - 1);
3097
        goto cleanup;
3098 3099 3100 3101
    }

    /* we get percpu cputime accounting info. */
    if (virCgroupGetCpuacctPercpuUsage(group, &buf))
3102
        goto cleanup;
3103 3104 3105 3106 3107 3108
    pos = buf;

    /* return percpu cputime in index 0 */
    param_idx = 0;

    /* number of cpus to compute */
J
Ján Tomko 已提交
3109
    need_cpus = MIN(total_cpus, start_cpu + ncpus);
3110

J
Ján Tomko 已提交
3111
    for (i = 0; i < need_cpus; i++) {
J
Ján Tomko 已提交
3112
        if (!virBitmapIsBitSet(cpumap, i)) {
3113 3114
            cpu_time = 0;
        } else if (virStrToLong_ull(pos, &pos, 10, &cpu_time) < 0) {
3115 3116
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cpuacct parse error"));
3117
            goto cleanup;
3118 3119 3120 3121 3122 3123
        }
        if (i < start_cpu)
            continue;
        ent = &params[(i - start_cpu) * nparams + param_idx];
        if (virTypedParameterAssign(ent, VIR_DOMAIN_CPU_STATS_CPUTIME,
                                    VIR_TYPED_PARAM_ULLONG, cpu_time) < 0)
3124
            goto cleanup;
3125 3126
    }

3127
    /* return percpu vcputime in index 1 */
3128
    param_idx = 1;
3129

3130
    if (guestvcpus && param_idx < nparams) {
3131
        if (VIR_ALLOC_N(sum_cpu_time, need_cpus) < 0)
3132
            goto cleanup;
3133 3134
        if (virCgroupGetPercpuVcpuSum(group, guestvcpus, sum_cpu_time,
                                      need_cpus, cpumap) < 0)
3135
            goto cleanup;
3136 3137 3138 3139 3140 3141 3142

        for (i = start_cpu; i < need_cpus; i++) {
            if (virTypedParameterAssign(&params[(i - start_cpu) * nparams +
                                                param_idx],
                                        VIR_DOMAIN_CPU_STATS_VCPUTIME,
                                        VIR_TYPED_PARAM_ULLONG,
                                        sum_cpu_time[i]) < 0)
3143
                goto cleanup;
3144 3145 3146
        }

        param_idx++;
3147 3148
    }

3149 3150 3151 3152 3153
    ret = param_idx;

 cleanup:
    virBitmapFree(cpumap);
    return ret;
3154 3155
}

3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205

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(&params[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(&params[1],
                                    VIR_DOMAIN_CPU_STATS_USERTIME,
                                    VIR_TYPED_PARAM_ULLONG, user) < 0)
            return -1;
        if (nparams > 2 &&
            virTypedParameterAssign(&params[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;
}


E
Eric Blake 已提交
3206 3207
int
virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares)
3208
{
3209 3210
    return virCgroupSetValueU64(group,
                                VIR_CGROUP_CONTROLLER_CPU,
D
Daniel P. Berrange 已提交
3211
                                "cpu.shares", shares);
3212 3213
}

E
Eric Blake 已提交
3214 3215 3216

int
virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares)
3217
{
3218 3219
    return virCgroupGetValueU64(group,
                                VIR_CGROUP_CONTROLLER_CPU,
D
Daniel P. Berrange 已提交
3220
                                "cpu.shares", shares);
3221
}
3222

E
Eric Blake 已提交
3223

3224 3225 3226 3227 3228 3229 3230 3231
/**
 * virCgroupSetCpuCfsPeriod:
 *
 * @group: The cgroup to change cpu.cfs_period_us for
 * @cfs_period: The bandwidth period in usecs
 *
 * Returns: 0 on success
 */
E
Eric Blake 已提交
3232 3233
int
virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
3234
{
3235
    /* The cfs_period should be greater or equal than 1ms, and less or equal
3236 3237
     * than 1s.
     */
3238 3239 3240 3241 3242 3243
    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;
    }
3244 3245 3246 3247 3248 3249

    return virCgroupSetValueU64(group,
                                VIR_CGROUP_CONTROLLER_CPU,
                                "cpu.cfs_period_us", cfs_period);
}

E
Eric Blake 已提交
3250

3251 3252 3253 3254 3255 3256 3257 3258
/**
 * 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
 */
E
Eric Blake 已提交
3259 3260
int
virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
3261 3262 3263 3264 3265 3266
{
    return virCgroupGetValueU64(group,
                                VIR_CGROUP_CONTROLLER_CPU,
                                "cpu.cfs_period_us", cfs_period);
}

E
Eric Blake 已提交
3267

3268 3269 3270 3271 3272 3273 3274 3275 3276
/**
 * 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
 */
E
Eric Blake 已提交
3277 3278
int
virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
3279
{
3280 3281 3282 3283 3284 3285 3286 3287
    /* 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;
3288 3289 3290 3291 3292 3293 3294
    }

    return virCgroupSetValueI64(group,
                                VIR_CGROUP_CONTROLLER_CPU,
                                "cpu.cfs_quota_us", cfs_quota);
}

E
Eric Blake 已提交
3295 3296 3297

int
virCgroupGetCpuacctPercpuUsage(virCgroupPtr group, char **usage)
3298 3299 3300 3301 3302
{
    return virCgroupGetValueStr(group, VIR_CGROUP_CONTROLLER_CPUACCT,
                                "cpuacct.usage_percpu", usage);
}

E
Eric Blake 已提交
3303

E
Eric Blake 已提交
3304 3305 3306 3307 3308 3309
int
virCgroupRemoveRecursively(char *grppath)
{
    DIR *grpdir;
    struct dirent *ent;
    int rc = 0;
E
Eric Blake 已提交
3310
    int direrr;
E
Eric Blake 已提交
3311

J
Ján Tomko 已提交
3312
    if (virDirOpenQuiet(&grpdir, grppath) < 0) {
E
Eric Blake 已提交
3313 3314 3315 3316 3317 3318 3319
        if (errno == ENOENT)
            return 0;
        rc = -errno;
        VIR_ERROR(_("Unable to open %s (%d)"), grppath, errno);
        return rc;
    }

E
Eric Blake 已提交
3320 3321 3322
    /* 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) {
3323
        VIR_AUTOFREE(char *) path = NULL;
E
Eric Blake 已提交
3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334

        if (ent->d_type != DT_DIR) continue;

        if (virAsprintf(&path, "%s/%s", grppath, ent->d_name) == -1) {
            rc = -ENOMEM;
            break;
        }
        rc = virCgroupRemoveRecursively(path);
        if (rc != 0)
            break;
    }
E
Eric Blake 已提交
3335 3336 3337 3338 3339
    if (direrr < 0) {
        rc = -errno;
        VIR_ERROR(_("Failed to readdir for %s (%d)"), grppath, errno);
    }

J
Ján Tomko 已提交
3340
    VIR_DIR_CLOSE(grpdir);
E
Eric Blake 已提交
3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371

    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;

    VIR_DEBUG("Removing cgroup %s", group->path);
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
3372 3373
        VIR_AUTOFREE(char *) grppath = NULL;

E
Eric Blake 已提交
3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401
        /* 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_DEBUG("Done removing cgroup %s", group->path);

    return rc;
}


3402 3403 3404
/*
 * Returns 1 if some PIDs are killed, 0 if none are killed, or -1 on error
 */
E
Eric Blake 已提交
3405 3406
static int
virCgroupKillInternal(virCgroupPtr group, int signum, virHashTablePtr pids)
3407
{
3408
    int ret = -1;
3409
    bool killedAny = false;
3410
    VIR_AUTOFREE(char *) keypath = NULL;
3411
    bool done = false;
E
Eric Blake 已提交
3412 3413 3414
    FILE *fp = NULL;
    VIR_DEBUG("group=%p path=%s signum=%d pids=%p",
              group, group->path, signum, pids);
3415

3416
    if (virCgroupPathOfController(group, -1, "tasks", &keypath) < 0)
3417
        return -1;
3418 3419 3420 3421 3422 3423 3424

    /* 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"))) {
3425 3426 3427 3428 3429 3430
            if (errno == ENOENT) {
                VIR_DEBUG("No file %s, assuming done", keypath);
                killedAny = false;
                goto done;
            }

3431 3432 3433
            virReportSystemError(errno,
                                 _("Failed to read %s"),
                                 keypath);
3434 3435 3436
            goto cleanup;
        } else {
            while (!feof(fp)) {
M
Michal Privoznik 已提交
3437 3438
                long pid_value;
                if (fscanf(fp, "%ld", &pid_value) != 1) {
3439 3440
                    if (feof(fp))
                        break;
3441 3442 3443
                    virReportSystemError(errno,
                                         _("Failed to read %s"),
                                         keypath);
E
Eric Blake 已提交
3444
                    goto cleanup;
3445
                }
3446
                if (virHashLookup(pids, (void*)pid_value))
3447 3448
                    continue;

M
Michal Privoznik 已提交
3449
                VIR_DEBUG("pid=%ld", pid_value);
3450 3451
                /* Cgroups is a Linux concept, so this cast is safe.  */
                if (kill((pid_t)pid_value, signum) < 0) {
3452
                    if (errno != ESRCH) {
3453
                        virReportSystemError(errno,
M
Michal Privoznik 已提交
3454
                                             _("Failed to kill process %ld"),
3455
                                             pid_value);
3456 3457 3458 3459
                        goto cleanup;
                    }
                    /* Leave RC == 0 since we didn't kill one */
                } else {
3460
                    killedAny = true;
3461 3462 3463
                    done = false;
                }

3464
                ignore_value(virHashAddEntry(pids, (void*)pid_value, (void*)1));
3465 3466 3467 3468 3469
            }
            VIR_FORCE_FCLOSE(fp);
        }
    }

3470
 done:
3471
    ret = killedAny ? 1 : 0;
3472

3473
 cleanup:
E
Eric Blake 已提交
3474
    VIR_FORCE_FCLOSE(fp);
3475

3476
    return ret;
3477 3478 3479
}


E
Eric Blake 已提交
3480 3481
static uint32_t
virCgroupPidCode(const void *name, uint32_t seed)
3482
{
M
Michal Privoznik 已提交
3483
    long pid_value = (long)(intptr_t)name;
3484
    return virHashCodeGen(&pid_value, sizeof(pid_value), seed);
3485
}
E
Eric Blake 已提交
3486 3487 3488 3489


static bool
virCgroupPidEqual(const void *namea, const void *nameb)
3490 3491 3492
{
    return namea == nameb;
}
E
Eric Blake 已提交
3493 3494 3495 3496


static void *
virCgroupPidCopy(const void *name)
3497 3498 3499 3500
{
    return (void*)name;
}

E
Eric Blake 已提交
3501

3502
/*
3503
 * Returns 1 if some PIDs are killed, 0 if none are killed, or -1 on error
3504
 */
E
Eric Blake 已提交
3505 3506
int
virCgroupKill(virCgroupPtr group, int signum)
3507 3508
{
    VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
3509
    int ret;
3510 3511 3512 3513
    /* The 'tasks' file in cgroups can contain duplicated
     * pids, so we use a hash to track which we've already
     * killed.
     */
3514 3515 3516 3517 3518 3519 3520 3521
    virHashTablePtr pids = virHashCreateFull(100,
                                             NULL,
                                             virCgroupPidCode,
                                             virCgroupPidEqual,
                                             virCgroupPidCopy,
                                             NULL);

    ret = virCgroupKillInternal(group, signum, pids);
3522

3523 3524 3525
    virHashFree(pids);

    return ret;
3526 3527 3528
}


E
Eric Blake 已提交
3529 3530 3531 3532 3533
static int
virCgroupKillRecursiveInternal(virCgroupPtr group,
                               int signum,
                               virHashTablePtr pids,
                               bool dormdir)
3534
{
3535
    int ret = -1;
3536
    int rc;
3537
    bool killedAny = false;
3538
    VIR_AUTOFREE(char *) keypath = NULL;
3539
    DIR *dp = NULL;
3540
    virCgroupPtr subgroup = NULL;
3541
    struct dirent *ent;
E
Eric Blake 已提交
3542
    int direrr;
E
Eric Blake 已提交
3543 3544
    VIR_DEBUG("group=%p path=%s signum=%d pids=%p",
              group, group->path, signum, pids);
3545

3546
    if (virCgroupPathOfController(group, -1, "", &keypath) < 0)
3547
        return -1;
3548

3549
    if ((rc = virCgroupKillInternal(group, signum, pids)) < 0)
3550
        goto cleanup;
3551 3552
    if (rc == 1)
        killedAny = true;
3553

3554
    VIR_DEBUG("Iterate over children of %s (killedAny=%d)", keypath, killedAny);
J
Ján Tomko 已提交
3555
    if ((rc = virDirOpenIfExists(&dp, keypath)) < 0)
3556
        goto cleanup;
J
Ján Tomko 已提交
3557 3558 3559 3560 3561

    if (rc == 0) {
        VIR_DEBUG("Path %s does not exist, assuming done", keypath);
        killedAny = false;
        goto done;
3562 3563
    }

E
Eric Blake 已提交
3564
    while ((direrr = virDirRead(dp, &ent, keypath)) > 0) {
3565 3566 3567 3568 3569
        if (ent->d_type != DT_DIR)
            continue;

        VIR_DEBUG("Process subdir %s", ent->d_name);

3570
        if (virCgroupNew(-1, ent->d_name, group, -1, &subgroup) < 0)
3571 3572
            goto cleanup;

E
Eric Blake 已提交
3573 3574
        if ((rc = virCgroupKillRecursiveInternal(subgroup, signum, pids,
                                                 true)) < 0)
3575 3576
            goto cleanup;
        if (rc == 1)
3577
            killedAny = true;
3578 3579 3580

        if (dormdir)
            virCgroupRemove(subgroup);
3581

3582
        virCgroupFree(&subgroup);
3583
    }
E
Eric Blake 已提交
3584 3585
    if (direrr < 0)
        goto cleanup;
3586

3587
 done:
3588
    ret = killedAny ? 1 : 0;
3589

3590
 cleanup:
3591
    virCgroupFree(&subgroup);
J
Ján Tomko 已提交
3592
    VIR_DIR_CLOSE(dp);
3593
    return ret;
3594 3595
}

E
Eric Blake 已提交
3596 3597 3598

int
virCgroupKillRecursive(virCgroupPtr group, int signum)
3599
{
3600
    int ret;
3601
    VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
3602 3603 3604 3605 3606 3607
    virHashTablePtr pids = virHashCreateFull(100,
                                             NULL,
                                             virCgroupPidCode,
                                             virCgroupPidEqual,
                                             virCgroupPidCopy,
                                             NULL);
3608

3609 3610 3611 3612 3613
    ret = virCgroupKillRecursiveInternal(group, signum, pids, false);

    virHashFree(pids);

    return ret;
3614 3615 3616
}


E
Eric Blake 已提交
3617 3618
int
virCgroupKillPainfully(virCgroupPtr group)
3619
{
3620
    size_t i;
3621
    int ret;
3622
    VIR_DEBUG("cgroup=%p path=%s", group, group->path);
3623
    for (i = 0; i < 15; i++) {
3624 3625 3626 3627 3628 3629
        int signum;
        if (i == 0)
            signum = SIGTERM;
        else if (i == 8)
            signum = SIGKILL;
        else
J
Ján Tomko 已提交
3630
            signum = 0; /* Just check for existence */
3631

3632 3633 3634 3635
        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)
3636 3637 3638 3639
            break;

        usleep(200 * 1000);
    }
3640 3641
    VIR_DEBUG("Complete %d", ret);
    return ret;
3642
}
3643

E
Eric Blake 已提交
3644 3645 3646

static char *
virCgroupIdentifyRoot(virCgroupPtr group)
3647 3648 3649 3650
{
    char *ret = NULL;
    size_t i;

3651
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
3652 3653 3654 3655 3656 3657 3658 3659 3660 3661
        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;
        }

3662 3663 3664
        if (VIR_STRNDUP(ret, group->controllers[i].mountPoint,
                        tmp - group->controllers[i].mountPoint) < 0)
            return NULL;
3665 3666 3667 3668 3669 3670 3671 3672 3673
        return ret;
    }

    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Could not find any mounted controllers"));
    return NULL;
}


3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704
/**
 * 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)
{
3705
    VIR_AUTOFREE(char *) str = NULL;
3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717
    char *p;
    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);
3718
        return -1;
3719 3720 3721 3722 3723 3724
    }
    if (!(p = STRSKIP(p, "\nsystem ")) ||
        virStrToLong_ull(p, NULL, 10, sys) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Cannot parse sys stat '%s'"),
                       p);
3725
        return -1;
3726 3727 3728 3729 3730 3731 3732 3733 3734
    }
    /* 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"));
3735
            return -1;
3736 3737 3738 3739 3740 3741
        }
        scale = 1000000000.0 / ticks_per_sec;
    }
    *user *= scale;
    *sys *= scale;

3742
    return 0;
3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763
}


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);
}


E
Eric Blake 已提交
3764
int
3765 3766
virCgroupBindMount(virCgroupPtr group, const char *oldroot,
                   const char *mountopts)
3767 3768
{
    size_t i;
3769 3770
    VIR_AUTOFREE(char *) opts = NULL;
    VIR_AUTOFREE(char *) root = NULL;
3771 3772 3773 3774 3775 3776 3777 3778 3779 3780

    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);
3781
        return -1;
3782 3783 3784
    }

    if (virAsprintf(&opts,
3785
                    "mode=755,size=65536%s", mountopts) < 0)
3786
        return -1;
3787 3788 3789 3790 3791

    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");
3792
        return -1;
3793 3794
    }

3795
    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
3796 3797 3798 3799
        if (!group->controllers[i].mountPoint)
            continue;

        if (!virFileExists(group->controllers[i].mountPoint)) {
3800
            VIR_AUTOFREE(char *) src = NULL;
3801
            if (virAsprintf(&src, "%s%s",
3802
                            oldroot,
3803
                            group->controllers[i].mountPoint) < 0)
3804
                return -1;
3805

E
Eric Blake 已提交
3806 3807
            VIR_DEBUG("Create mount point '%s'",
                      group->controllers[i].mountPoint);
3808 3809 3810 3811
            if (virFileMakePath(group->controllers[i].mountPoint) < 0) {
                virReportSystemError(errno,
                                     _("Unable to create directory %s"),
                                     group->controllers[i].mountPoint);
3812
                return -1;
3813 3814
            }

3815
            if (mount(src, group->controllers[i].mountPoint, "none", MS_BIND,
E
Eric Blake 已提交
3816
                      NULL) < 0) {
3817 3818 3819
                virReportSystemError(errno,
                                     _("Failed to bind cgroup '%s' on '%s'"),
                                     src, group->controllers[i].mountPoint);
3820
                return -1;
3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833
            }
        }

        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);
3834
                return -1;
3835 3836 3837 3838
            }
        }
    }

3839
    return 0;
3840
}
3841 3842


3843 3844 3845 3846 3847 3848 3849 3850
int virCgroupSetOwner(virCgroupPtr cgroup,
                      uid_t uid,
                      gid_t gid,
                      int controllers)
{
    int ret = -1;
    size_t i;
    DIR *dh = NULL;
E
Eric Blake 已提交
3851
    int direrr;
3852 3853

    for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
3854
        VIR_AUTOFREE(char *) base = NULL;
3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866
        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;

J
Ján Tomko 已提交
3867
        if (virDirOpen(&dh, base) < 0)
3868 3869
            goto cleanup;

E
Eric Blake 已提交
3870
        while ((direrr = virDirRead(dh, &de, base)) > 0) {
3871 3872
            VIR_AUTOFREE(char *) entry = NULL;

3873 3874 3875 3876 3877 3878 3879 3880 3881 3882
            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;
            }
        }
E
Eric Blake 已提交
3883 3884
        if (direrr < 0)
            goto cleanup;
3885 3886 3887 3888 3889 3890 3891 3892

        if (chown(base, uid, gid) < 0) {
            virReportSystemError(errno,
                                 _("cannot chown '%s' to (%u, %u)"),
                                 base, uid, gid);
            goto cleanup;
        }

J
Ján Tomko 已提交
3893
        VIR_DIR_CLOSE(dh);
3894 3895 3896 3897 3898
    }

    ret = 0;

 cleanup:
J
Ján Tomko 已提交
3899
    VIR_DIR_CLOSE(dh);
3900 3901 3902 3903
    return ret;
}


3904 3905 3906 3907 3908 3909 3910 3911 3912 3913
/**
 * 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)
{
3914
    VIR_AUTOFREE(char *) path = NULL;
3915 3916 3917 3918 3919 3920 3921

    if (!cgroup)
        return false;

    if (virCgroupPathOfController(cgroup, VIR_CGROUP_CONTROLLER_CPU,
                                  "cpu.cfs_period_us", &path) < 0) {
        virResetLastError();
3922
        return false;
3923 3924
    }

3925
    return virFileExists(path);
3926 3927
}

3928 3929 3930 3931
int
virCgroupHasEmptyTasks(virCgroupPtr cgroup, int controller)
{
    int ret = -1;
3932
    VIR_AUTOFREE(char *) content = NULL;
3933

3934 3935 3936
    if (!cgroup)
        return -1;

3937 3938 3939 3940 3941 3942 3943
    ret = virCgroupGetValueStr(cgroup, controller, "tasks", &content);

    if (ret == 0 && content[0] == '\0')
        ret = 1;

    return ret;
}
3944

3945 3946 3947
bool
virCgroupControllerAvailable(int controller)
{
3948 3949
    virCgroupPtr cgroup;
    bool ret = false;
3950 3951

    if (virCgroupNewSelf(&cgroup) < 0)
3952
        return ret;
3953

3954
    ret = virCgroupHasController(cgroup, controller);
3955
    virCgroupFree(&cgroup);
3956
    return ret;
3957 3958
}

3959 3960
#else /* !VIR_CGROUP_SUPPORTED */

3961 3962 3963 3964 3965 3966 3967
bool
virCgroupAvailable(void)
{
    return false;
}


3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978
int
virCgroupDetectMountsFromFile(virCgroupPtr group ATTRIBUTE_UNUSED,
                              const char *path ATTRIBUTE_UNUSED,
                              bool checkLinks ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012
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;
}


4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025
int
virCgroupNewThread(virCgroupPtr domain ATTRIBUTE_UNUSED,
                   virCgroupThreadName nameval ATTRIBUTE_UNUSED,
                   int id ATTRIBUTE_UNUSED,
                   bool create ATTRIBUTE_UNUSED,
                   virCgroupPtr *group ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036
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;
}


4037 4038 4039 4040 4041
int
virCgroupNewDetectMachine(const char *name ATTRIBUTE_UNUSED,
                          const char *drivername ATTRIBUTE_UNUSED,
                          pid_t pid ATTRIBUTE_UNUSED,
                          int controllers ATTRIBUTE_UNUSED,
4042
                          char *machinename ATTRIBUTE_UNUSED,
4043 4044 4045 4046 4047 4048 4049
                          virCgroupPtr *group ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

J
Ján Tomko 已提交
4050

4051
int virCgroupTerminateMachine(const char *name ATTRIBUTE_UNUSED)
J
Ján Tomko 已提交
4052 4053 4054 4055 4056 4057 4058
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4059 4060 4061 4062 4063 4064 4065
int
virCgroupNewMachine(const char *name ATTRIBUTE_UNUSED,
                    const char *drivername ATTRIBUTE_UNUSED,
                    const unsigned char *uuid ATTRIBUTE_UNUSED,
                    const char *rootdir ATTRIBUTE_UNUSED,
                    pid_t pidleader ATTRIBUTE_UNUSED,
                    bool isContainer ATTRIBUTE_UNUSED,
4066 4067
                    size_t nnicindexes ATTRIBUTE_UNUSED,
                    int *nicindexes ATTRIBUTE_UNUSED,
4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084
                    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;
}

4085 4086

void
4087
virCgroupFree(virCgroupPtr *group ATTRIBUTE_UNUSED)
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
}


bool
virCgroupHasController(virCgroupPtr cgroup ATTRIBUTE_UNUSED,
                       int controller ATTRIBUTE_UNUSED)
{
    return false;
}


4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113
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;
}


4114 4115 4116 4117 4118 4119 4120 4121 4122 4123
int
virCgroupAddTask(virCgroupPtr group ATTRIBUTE_UNUSED,
                 pid_t pid ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4124 4125 4126 4127 4128 4129 4130 4131 4132 4133
int
virCgroupAddMachineTask(virCgroupPtr group ATTRIBUTE_UNUSED,
                        pid_t pid ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144
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;
}


4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171
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;
}


4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
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;
}


4192 4193 4194 4195 4196 4197 4198 4199 4200 4201
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;
}

4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241
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;
}

4242 4243 4244
int
virCgroupGetBlkioDeviceWeight(virCgroupPtr group ATTRIBUTE_UNUSED,
                              const char *path ATTRIBUTE_UNUSED,
4245
                              unsigned int *weight ATTRIBUTE_UNUSED)
4246 4247 4248 4249 4250 4251 4252 4253 4254
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

int
virCgroupGetBlkioDeviceReadIops(virCgroupPtr group ATTRIBUTE_UNUSED,
                                const char *path ATTRIBUTE_UNUSED,
4255
                                unsigned int *riops ATTRIBUTE_UNUSED)
4256 4257 4258 4259 4260 4261 4262 4263 4264
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

int
virCgroupGetBlkioDeviceWriteIops(virCgroupPtr group ATTRIBUTE_UNUSED,
                                 const char *path ATTRIBUTE_UNUSED,
4265
                                 unsigned int *wiops ATTRIBUTE_UNUSED)
4266 4267 4268 4269 4270 4271 4272 4273 4274
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

int
virCgroupGetBlkioDeviceReadBps(virCgroupPtr group ATTRIBUTE_UNUSED,
                               const char *path ATTRIBUTE_UNUSED,
4275
                               unsigned long long *rbps ATTRIBUTE_UNUSED)
4276 4277 4278 4279 4280 4281 4282 4283 4284
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

int
virCgroupGetBlkioDeviceWriteBps(virCgroupPtr group ATTRIBUTE_UNUSED,
                                const char *path ATTRIBUTE_UNUSED,
4285
                                unsigned long long *wbps ATTRIBUTE_UNUSED)
4286 4287 4288 4289 4290
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}
4291

4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400
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;
}

4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420

int
virCgroupSetCpusetMemoryMigrate(virCgroupPtr group ATTRIBUTE_UNUSED,
                                bool migrate ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


int
virCgroupGetCpusetMemoryMigrate(virCgroupPtr group ATTRIBUTE_UNUSED,
                                bool *migrate ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440

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;
}

4441 4442 4443 4444 4445 4446 4447 4448
int
virCgroupAllowAllDevices(virCgroupPtr group ATTRIBUTE_UNUSED,
                         int perms ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}
4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471

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;
}


4472 4473 4474
int
virCgroupAllowDevicePath(virCgroupPtr group ATTRIBUTE_UNUSED,
                         const char *path ATTRIBUTE_UNUSED,
4475 4476
                         int perms ATTRIBUTE_UNUSED,
                         bool ignoreEaccess ATTRIBUTE_UNUSED)
4477 4478 4479 4480 4481 4482 4483
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496
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;
}


4497 4498 4499
int
virCgroupDenyDevicePath(virCgroupPtr group ATTRIBUTE_UNUSED,
                        const char *path ATTRIBUTE_UNUSED,
4500 4501
                        int perms ATTRIBUTE_UNUSED,
                        bool ignoreEacces ATTRIBUTE_UNUSED)
4502 4503 4504 4505 4506 4507 4508
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558
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;
}


4559 4560 4561 4562 4563 4564 4565 4566 4567
int
virCgroupRemoveRecursively(char *grppath ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4568 4569 4570 4571 4572 4573 4574 4575 4576
int
virCgroupRemove(virCgroupPtr group ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENXIO, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}


4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605
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;
}


4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646
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;
}


4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657
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;
}


4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677
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;
}


E
Eric Blake 已提交
4678
int
4679 4680 4681
virCgroupBindMount(virCgroupPtr group ATTRIBUTE_UNUSED,
                   const char *oldroot ATTRIBUTE_UNUSED,
                   const char *mountopts ATTRIBUTE_UNUSED)
4682
{
4683 4684 4685
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
4686
}
4687

4688 4689 4690 4691 4692 4693 4694 4695

bool
virCgroupSupportsCpuBW(virCgroupPtr cgroup ATTRIBUTE_UNUSED)
{
    VIR_DEBUG("Control groups not supported on this platform");
    return false;
}

E
Eric Blake 已提交
4696 4697 4698 4699 4700 4701

int
virCgroupGetPercpuStats(virCgroupPtr group ATTRIBUTE_UNUSED,
                        virTypedParameterPtr params ATTRIBUTE_UNUSED,
                        unsigned int nparams ATTRIBUTE_UNUSED,
                        int start_cpu ATTRIBUTE_UNUSED,
J
Ján Tomko 已提交
4702
                        unsigned int ncpus ATTRIBUTE_UNUSED,
4703
                        virBitmapPtr guestvcpus ATTRIBUTE_UNUSED)
E
Eric Blake 已提交
4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721
{
    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;
}

4722 4723 4724 4725 4726 4727 4728 4729 4730
int
virCgroupHasEmptyTasks(virCgroupPtr cgroup ATTRIBUTE_UNUSED,
                       int controller ATTRIBUTE_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Control groups not supported on this platform"));
    return -1;
}

4731 4732 4733 4734 4735
bool
virCgroupControllerAvailable(int controller ATTRIBUTE_UNUSED)
{
    return false;
}
4736
#endif /* !VIR_CGROUP_SUPPORTED */
4737 4738 4739 4740 4741 4742 4743


int
virCgroupDelThread(virCgroupPtr cgroup,
                   virCgroupThreadName nameval,
                   int idx)
{
4744
    virCgroupPtr new_cgroup = NULL;
4745 4746 4747 4748 4749 4750 4751

    if (cgroup) {
        if (virCgroupNewThread(cgroup, nameval, idx, false, &new_cgroup) < 0)
            return -1;

        /* Remove the offlined cgroup */
        virCgroupRemove(new_cgroup);
4752
        virCgroupFree(&new_cgroup);
4753 4754 4755 4756
    }

    return 0;
}