qemu_cgroup.c 36.6 KB
Newer Older
1 2 3
/*
 * qemu_cgroup.c: QEMU cgroup management
 *
4
 * Copyright (C) 2006-2015 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * 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
 */

#include <config.h>

#include "qemu_cgroup.h"
25
#include "qemu_domain.h"
26
#include "qemu_process.h"
27
#include "qemu_extdevice.h"
28
#include "qemu_hostdev.h"
29
#include "virlog.h"
30
#include "viralloc.h"
31
#include "virerror.h"
32
#include "domain_audit.h"
33
#include "domain_cgroup.h"
34
#include "virscsi.h"
35
#include "virstring.h"
36
#include "virfile.h"
37
#include "virtypedparam.h"
38
#include "virnuma.h"
39
#include "virsystemd.h"
40
#include "virdevmapper.h"
J
Ján Tomko 已提交
41
#include "virutil.h"
42 43 44

#define VIR_FROM_THIS VIR_FROM_QEMU

45 46
VIR_LOG_INIT("qemu.qemu_cgroup");

47
const char *const defaultDeviceACL[] = {
48 49
    "/dev/null", "/dev/full", "/dev/zero",
    "/dev/random", "/dev/urandom",
50
    "/dev/ptmx", "/dev/kvm",
51
    "/dev/rtc", "/dev/hpet",
52 53 54 55 56
    NULL,
};
#define DEVICE_PTY_MAJOR 136
#define DEVICE_SND_MAJOR 116

57

58
static int
59 60 61
qemuSetupImagePathCgroup(virDomainObjPtr vm,
                         const char *path,
                         bool readonly)
62
{
63
    qemuDomainObjPrivatePtr priv = vm->privateData;
64
    int perms = VIR_CGROUP_DEVICE_READ;
65
    VIR_AUTOSTRINGLIST targetPaths = NULL;
66 67
    size_t i;
    int rv;
68

69
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
70 71
        return 0;

72
    if (!readonly)
73
        perms |= VIR_CGROUP_DEVICE_WRITE;
74

75
    VIR_DEBUG("Allow path %s, perms: %s",
76
              path, virCgroupGetDevicePermsString(perms));
77

78
    rv = virCgroupAllowDevicePath(priv->cgroup, path, perms, true);
79

80
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path,
81
                             virCgroupGetDevicePermsString(perms),
82 83
                             rv);
    if (rv < 0)
84
        return -1;
85 86 87

    if (rv > 0) {
        /* @path is neither character device nor block device. */
88
        return 0;
89 90 91 92 93 94 95
    }

    if (virDevMapperGetTargets(path, &targetPaths) < 0 &&
        errno != ENOSYS && errno != EBADF) {
        virReportSystemError(errno,
                             _("Unable to get devmapper targets for %s"),
                             path);
96
        return -1;
97
    }
98

99 100 101 102 103 104 105
    for (i = 0; targetPaths && targetPaths[i]; i++) {
        rv = virCgroupAllowDevicePath(priv->cgroup, targetPaths[i], perms, false);

        virDomainAuditCgroupPath(vm, priv->cgroup, "allow", targetPaths[i],
                                 virCgroupGetDevicePermsString(perms),
                                 rv);
        if (rv < 0)
106
            return -1;
107 108
    }

109
    return 0;
110 111 112
}


113 114 115 116 117
static int
qemuSetupImageCgroupInternal(virDomainObjPtr vm,
                             virStorageSourcePtr src,
                             bool forceReadonly)
{
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
    g_autofree char *path = NULL;
    bool readonly = src->readonly || forceReadonly;

    if (src->type == VIR_STORAGE_TYPE_NVME) {
        /* Even though disk is R/O we can't make it so in
         * CGroups. QEMU will try to do some ioctl()-s over the
         * device and such operations are considered R/W by the
         * kernel */
        readonly = false;

        if (!(path = virPCIDeviceAddressGetIOMMUGroupDev(&src->nvme->pciAddr)))
            return -1;

        if (qemuSetupImagePathCgroup(vm, QEMU_DEV_VFIO, false) < 0)
            return -1;
    } else {
        if (!src->path || !virStorageSourceIsLocalStorage(src)) {
            VIR_DEBUG("Not updating cgroups for disk path '%s', type: %s",
                      NULLSTR(src->path), virStorageTypeToString(src->type));
            return 0;
        }

        path = g_strdup(src->path);
141 142
    }

143
    if (virStoragePRDefIsManaged(src->pr) &&
144 145
        virFileExists(QEMU_DEVICE_MAPPER_CONTROL_PATH) &&
        qemuSetupImagePathCgroup(vm, QEMU_DEVICE_MAPPER_CONTROL_PATH, false) < 0)
146 147
        return -1;

148
    return qemuSetupImagePathCgroup(vm, path, readonly);
149 150 151
}


152
int
153 154
qemuSetupImageCgroup(virDomainObjPtr vm,
                     virStorageSourcePtr src)
155
{
156
    return qemuSetupImageCgroupInternal(vm, src, false);
157 158 159 160 161 162 163
}


int
qemuTeardownImageCgroup(virDomainObjPtr vm,
                        virStorageSourcePtr src)
{
164
    qemuDomainObjPrivatePtr priv = vm->privateData;
165
    g_autofree char *path = NULL;
166
    int perms = VIR_CGROUP_DEVICE_RWM;
167 168
    bool hasPR = false;
    bool hasNVMe = false;
169
    size_t i;
170 171 172 173 174 175
    int ret;

    if (!virCgroupHasController(priv->cgroup,
                                VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

176 177
    for (i = 0; i < vm->def->ndisks; i++) {
        virStorageSourcePtr diskSrc = vm->def->disks[i]->src;
178

179 180
        if (src == diskSrc)
            continue;
181

182 183
        if (virStoragePRDefIsManaged(diskSrc->pr))
            hasPR = true;
184

185 186 187 188 189 190 191
        if (virStorageSourceChainHasNVMe(diskSrc))
            hasNVMe = true;
    }

    if (src->type == VIR_STORAGE_TYPE_NVME) {
        if (!(path = virPCIDeviceAddressGetIOMMUGroupDev(&src->nvme->pciAddr)))
            return -1;
192

193 194 195
        if (!hasNVMe &&
            !qemuDomainNeedsVFIO(vm->def)) {
            ret = virCgroupDenyDevicePath(priv->cgroup, QEMU_DEV_VFIO, perms, true);
196
            virDomainAuditCgroupPath(vm, priv->cgroup, "deny",
197
                                     QEMU_DEV_VFIO,
198 199
                                     virCgroupGetDevicePermsString(perms), ret);
            if (ret < 0)
200 201 202 203 204 205 206
                return -1;
        }
    } else {
        if (!src->path || !virStorageSourceIsLocalStorage(src)) {
            VIR_DEBUG("Not updating cgroups for disk path '%s', type: %s",
                      NULLSTR(src->path), virStorageTypeToString(src->type));
            return 0;
207
        }
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222

        path = g_strdup(src->path);
    }

    if (!hasPR &&
        virFileExists(QEMU_DEVICE_MAPPER_CONTROL_PATH)) {
        VIR_DEBUG("Disabling device mapper control");
        ret = virCgroupDenyDevicePath(priv->cgroup,
                                      QEMU_DEVICE_MAPPER_CONTROL_PATH,
                                      perms, true);
        virDomainAuditCgroupPath(vm, priv->cgroup, "deny",
                                 QEMU_DEVICE_MAPPER_CONTROL_PATH,
                                 virCgroupGetDevicePermsString(perms), ret);
        if (ret < 0)
            return ret;
223 224
    }

225
    VIR_DEBUG("Deny path %s", path);
226

227
    ret = virCgroupDenyDevicePath(priv->cgroup, path, perms, true);
228

229
    virDomainAuditCgroupPath(vm, priv->cgroup, "deny", path,
230
                             virCgroupGetDevicePermsString(perms), ret);
231

232 233 234 235 236 237 238
    /* If you're looking for a counter part to
     * qemuSetupImagePathCgroup you're at the right place.
     * However, we can't just blindly deny all the device mapper
     * targets of src->path because they might still be used by
     * another disk in domain. Just like we are not removing
     * disks from namespace. */

239
    return ret;
240 241 242
}


243
int
244 245
qemuSetupImageChainCgroup(virDomainObjPtr vm,
                          virStorageSourcePtr src)
246
{
247
    virStorageSourcePtr next;
248
    bool forceReadonly = false;
249

250
    for (next = src; virStorageSourceIsBacking(next); next = next->backingStore) {
251
        if (qemuSetupImageCgroupInternal(vm, next, forceReadonly) < 0)
252
            return -1;
253 254 255

        /* setup only the top level image for read-write */
        forceReadonly = true;
256
    }
257 258

    return 0;
259 260 261
}


262
int
263 264
qemuTeardownImageChainCgroup(virDomainObjPtr vm,
                             virStorageSourcePtr src)
265
{
266
    virStorageSourcePtr next;
267

268
    for (next = src; virStorageSourceIsBacking(next); next = next->backingStore) {
269
        if (qemuTeardownImageCgroup(vm, next) < 0)
270 271
            return -1;
    }
272

273
    return 0;
274 275
}

276

277
static int
278
qemuSetupChrSourceCgroup(virDomainObjPtr vm,
279
                         virDomainChrSourceDefPtr source)
280
{
281
    qemuDomainObjPrivatePtr priv = vm->privateData;
282
    int ret;
283

284 285 286
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

287
    if (source->type != VIR_DOMAIN_CHR_TYPE_DEV)
288 289
        return 0;

290
    VIR_DEBUG("Process path '%s' for device", source->data.file.path);
291

292
    ret = virCgroupAllowDevicePath(priv->cgroup, source->data.file.path,
293
                                   VIR_CGROUP_DEVICE_RW, false);
294
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow",
295
                             source->data.file.path, "rw", ret);
296

297
    return ret;
298 299
}

300 301 302 303 304 305 306 307

static int
qemuTeardownChrSourceCgroup(virDomainObjPtr vm,
                            virDomainChrSourceDefPtr source)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int ret;

308 309 310
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

311 312 313 314 315 316 317 318
    if (source->type != VIR_DOMAIN_CHR_TYPE_DEV)
        return 0;

    VIR_DEBUG("Process path '%s' for device", source->data.file.path);

    ret = virCgroupDenyDevicePath(priv->cgroup, source->data.file.path,
                                  VIR_CGROUP_DEVICE_RW, false);
    virDomainAuditCgroupPath(vm, priv->cgroup, "deny",
319
                             source->data.file.path, "rw", ret);
320 321 322 323 324

    return ret;
}


325
static int
J
Ján Tomko 已提交
326
qemuSetupChardevCgroupCB(virDomainDefPtr def G_GNUC_UNUSED,
327 328
                         virDomainChrDefPtr dev,
                         void *opaque)
329
{
330 331
    virDomainObjPtr vm = opaque;

332
    return qemuSetupChrSourceCgroup(vm, dev->source);
333 334 335 336
}


static int
337
qemuSetupTPMCgroup(virDomainObjPtr vm)
338
{
339
    int ret = 0;
340
    virDomainTPMDefPtr dev = vm->def->tpm;
341 342 343

    switch (dev->type) {
    case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
344
        ret = qemuSetupChrSourceCgroup(vm, &dev->data.passthrough.source);
345
        break;
346
    case VIR_DOMAIN_TPM_TYPE_EMULATOR:
347 348 349 350
    case VIR_DOMAIN_TPM_TYPE_LAST:
        break;
    }

351
    return ret;
352 353
}

354

355
int
356 357 358 359 360 361
qemuSetupInputCgroup(virDomainObjPtr vm,
                     virDomainInputDefPtr dev)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int ret = 0;

362 363 364
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

365 366 367 368
    switch (dev->type) {
    case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
        VIR_DEBUG("Process path '%s' for input device", dev->source.evdev);
        ret = virCgroupAllowDevicePath(priv->cgroup, dev->source.evdev,
369
                                       VIR_CGROUP_DEVICE_RW, false);
370
        virDomainAuditCgroupPath(vm, priv->cgroup, "allow", dev->source.evdev, "rw", ret);
371 372 373 374 375 376 377
        break;
    }

    return ret;
}


378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
int
qemuTeardownInputCgroup(virDomainObjPtr vm,
                        virDomainInputDefPtr dev)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int ret = 0;

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

    switch (dev->type) {
    case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
        VIR_DEBUG("Process path '%s' for input device", dev->source.evdev);
        ret = virCgroupDenyDevicePath(priv->cgroup, dev->source.evdev,
                                      VIR_CGROUP_DEVICE_RWM, false);
393
        virDomainAuditCgroupPath(vm, priv->cgroup, "deny", dev->source.evdev, "rwm", ret);
394 395 396 397 398 399 400
        break;
    }

    return ret;
}


401 402 403 404 405 406 407 408 409 410
/**
 * qemuSetupHostdevCgroup:
 * vm: domain object
 * @dev: device to allow
 *
 * For given host device @dev allow access to in Cgroups.
 *
 * Returns: 0 on success,
 *         -1 otherwise.
 */
411
int
412
qemuSetupHostdevCgroup(virDomainObjPtr vm,
413 414 415
                       virDomainHostdevDefPtr dev)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
416 417
    g_autofree char *path = NULL;
    int perms;
418
    int rv;
419

420 421 422
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

423
    if (qemuDomainGetHostdevPath(dev, &path, &perms) < 0)
424
        return -1;
425

426 427 428 429 430 431 432 433 434
    if (path) {
        VIR_DEBUG("Cgroup allow %s perms=%d", path, perms);
        rv = virCgroupAllowDevicePath(priv->cgroup, path, perms, false);
        virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path,
                                 virCgroupGetDevicePermsString(perms),
                                 rv);
        if (rv < 0)
            return -1;
    }
435

436 437 438 439 440 441 442
    if (qemuHostdevNeedsVFIO(dev)) {
        VIR_DEBUG("Cgroup allow %s perms=%d", QEMU_DEV_VFIO, VIR_CGROUP_DEVICE_RW);
        rv = virCgroupAllowDevicePath(priv->cgroup, QEMU_DEV_VFIO,
                                      VIR_CGROUP_DEVICE_RW, false);
        virDomainAuditCgroupPath(vm, priv->cgroup, "allow",
                                 QEMU_DEV_VFIO, "rw", rv);
        if (rv < 0)
443
            return -1;
444 445
    }

446
    return 0;
447 448
}

449 450 451 452 453 454 455 456 457 458 459 460

/**
 * qemuTeardownHostdevCgroup:
 * @vm: doamin object
 * @dev: device to tear down
 *
 * For given host device @dev deny access to it in CGroups.
 * Note, @dev must not be in @vm's definition.
 *
 * Returns: 0 on success,
 *         -1 otherwise.
 */
461 462
int
qemuTeardownHostdevCgroup(virDomainObjPtr vm,
463
                          virDomainHostdevDefPtr dev)
464 465
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
466
    g_autofree char *path = NULL;
467
    int rv;
468 469 470 471

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

472
    if (qemuDomainGetHostdevPath(dev, &path, NULL) < 0)
473
        return -1;
474

475 476 477 478 479 480 481 482 483
    if (path) {
        VIR_DEBUG("Cgroup deny %s", path);
        rv = virCgroupDenyDevicePath(priv->cgroup, path,
                                     VIR_CGROUP_DEVICE_RWM, false);
        virDomainAuditCgroupPath(vm, priv->cgroup,
                                 "deny", path, "rwm", rv);
        if (rv < 0)
            return -1;
    }
484

485 486 487 488 489 490 491 492
    if (qemuHostdevNeedsVFIO(dev) &&
        !qemuDomainNeedsVFIO(vm->def)) {
        VIR_DEBUG("Cgroup deny " QEMU_DEV_VFIO);
        rv = virCgroupDenyDevicePath(priv->cgroup, QEMU_DEV_VFIO,
                                     VIR_CGROUP_DEVICE_RWM, false);
        virDomainAuditCgroupPath(vm, priv->cgroup, "deny",
                                 QEMU_DEV_VFIO, "rwm", rv);
        if (rv < 0)
493
            return -1;
494 495
    }

496
    return 0;
497 498
}

499

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
int
qemuSetupMemoryDevicesCgroup(virDomainObjPtr vm,
                             virDomainMemoryDefPtr mem)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int rv;

    if (mem->model != VIR_DOMAIN_MEMORY_MODEL_NVDIMM)
        return 0;

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

    VIR_DEBUG("Setting devices Cgroup for NVDIMM device: %s", mem->nvdimmPath);
    rv = virCgroupAllowDevicePath(priv->cgroup, mem->nvdimmPath,
                                  VIR_CGROUP_DEVICE_RW, false);
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow",
517
                             mem->nvdimmPath, "rw", rv);
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538

    return rv;
}


int
qemuTeardownMemoryDevicesCgroup(virDomainObjPtr vm,
                                virDomainMemoryDefPtr mem)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int rv;

    if (mem->model != VIR_DOMAIN_MEMORY_MODEL_NVDIMM)
        return 0;

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

    rv = virCgroupDenyDevicePath(priv->cgroup, mem->nvdimmPath,
                                 VIR_CGROUP_DEVICE_RWM, false);
    virDomainAuditCgroupPath(vm, priv->cgroup,
539
                             "deny", mem->nvdimmPath, "rwm", rv);
540 541 542 543
    return rv;
}


544 545 546 547 548
static int
qemuSetupGraphicsCgroup(virDomainObjPtr vm,
                        virDomainGraphicsDefPtr gfx)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
549
    const char *rendernode = virDomainGraphicsGetRenderNode(gfx);
550 551
    int ret;

552 553
    if (!rendernode ||
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
554 555 556 557 558
        return 0;

    ret = virCgroupAllowDevicePath(priv->cgroup, rendernode,
                                   VIR_CGROUP_DEVICE_RW, false);
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow", rendernode,
559
                             "rw", ret);
560 561 562 563
    return ret;
}


564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
static int
qemuSetupVideoCgroup(virDomainObjPtr vm,
                     virDomainVideoDefPtr def)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    virDomainVideoAccelDefPtr accel = def->accel;
    int ret;

    if (!accel)
        return 0;

    if (!accel->rendernode ||
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

    ret = virCgroupAllowDevicePath(priv->cgroup, accel->rendernode,
                                   VIR_CGROUP_DEVICE_RW, false);
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow", accel->rendernode,
                             "rw", ret);
    return ret;
}


587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
static int
qemuSetupBlkioCgroup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (!virCgroupHasController(priv->cgroup,
                                VIR_CGROUP_CONTROLLER_BLKIO)) {
        if (vm->def->blkio.weight || vm->def->blkio.ndevices) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("Block I/O tuning is not available on this host"));
            return -1;
        } else {
            return 0;
        }
    }

603
    return virDomainCgroupSetupBlkio(priv->cgroup, vm->def->blkio);
604 605
}

606

607 608 609 610 611
static int
qemuSetupMemoryCgroup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

E
Eric Blake 已提交
612
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_MEMORY)) {
613 614 615
        if (virMemoryLimitIsSet(vm->def->mem.hard_limit) ||
            virMemoryLimitIsSet(vm->def->mem.soft_limit) ||
            virMemoryLimitIsSet(vm->def->mem.swap_hard_limit)) {
616 617 618
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("Memory cgroup is not available on this host"));
            return -1;
O
Osier Yang 已提交
619 620
        } else {
            return 0;
621 622 623
        }
    }

624
    return virDomainCgroupSetupMemtune(priv->cgroup, vm->def->mem);
625 626 627
}


628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
static int
qemuSetupFirmwareCgroup(virDomainObjPtr vm)
{
    if (!vm->def->os.loader)
        return 0;

    if (vm->def->os.loader->path &&
        qemuSetupImagePathCgroup(vm, vm->def->os.loader->path,
                                 vm->def->os.loader->readonly == VIR_TRISTATE_BOOL_YES) < 0)
        return -1;

    if (vm->def->os.loader->nvram &&
        qemuSetupImagePathCgroup(vm, vm->def->os.loader->nvram, false) < 0)
        return -1;

    return 0;
}


647 648 649 650 651 652 653
int
qemuSetupRNGCgroup(virDomainObjPtr vm,
                   virDomainRNGDefPtr rng)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int rv;

654 655 656
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

657 658 659 660 661 662 663
    if (rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM) {
        VIR_DEBUG("Setting Cgroup ACL for RNG device");
        rv = virCgroupAllowDevicePath(priv->cgroup,
                                      rng->source.file,
                                      VIR_CGROUP_DEVICE_RW, false);
        virDomainAuditCgroupPath(vm, priv->cgroup, "allow",
                                 rng->source.file,
664
                                 "rw", rv);
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
        if (rv < 0 &&
            !virLastErrorIsSystemErrno(ENOENT))
            return -1;
    }

    return 0;
}


int
qemuTeardownRNGCgroup(virDomainObjPtr vm,
                      virDomainRNGDefPtr rng)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int rv;

681 682 683
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

684 685 686 687 688 689 690
    if (rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM) {
        VIR_DEBUG("Tearing down Cgroup ACL for RNG device");
        rv = virCgroupDenyDevicePath(priv->cgroup,
                                     rng->source.file,
                                     VIR_CGROUP_DEVICE_RW, false);
        virDomainAuditCgroupPath(vm, priv->cgroup, "deny",
                                 rng->source.file,
691
                                 "rw", rv);
692 693 694 695 696 697 698 699 700
        if (rv < 0 &&
            !virLastErrorIsSystemErrno(ENOENT))
            return -1;
    }

    return 0;
}


701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
int
qemuSetupChardevCgroup(virDomainObjPtr vm,
                       virDomainChrDefPtr dev)
{
    return qemuSetupChrSourceCgroup(vm, dev->source);
}


int
qemuTeardownChardevCgroup(virDomainObjPtr vm,
                          virDomainChrDefPtr dev)
{
    return qemuTeardownChrSourceCgroup(vm, dev->source);
}


717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
static int
qemuSetupSEVCgroup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    int ret;

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

    ret = virCgroupAllowDevicePath(priv->cgroup, "/dev/sev",
                                   VIR_CGROUP_DEVICE_RW, false);
    virDomainAuditCgroupPath(vm, priv->cgroup, "allow", "/dev/sev",
                             "rw", ret);
    return ret;
}

733
static int
734
qemuSetupDevicesCgroup(virDomainObjPtr vm)
735 736
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
737
    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
738
    const char *const *deviceACL = NULL;
739
    int rv = -1;
740
    size_t i;
741 742 743 744

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
        return 0;

745 746 747 748 749
    rv = virCgroupDenyAllDevices(priv->cgroup);
    virDomainAuditCgroup(vm, priv->cgroup, "deny", "all", rv == 0);
    if (rv < 0) {
        if (virLastErrorIsSystemErrno(EPERM)) {
            virResetLastError();
750 751 752 753
            VIR_WARN("Group devices ACL is not accessible, disabling whitelisting");
            return 0;
        }

754
        return -1;
755 756
    }

757
    if (qemuSetupFirmwareCgroup(vm) < 0)
758
        return -1;
759

760
    for (i = 0; i < vm->def->ndisks; i++) {
761
        if (qemuSetupImageChainCgroup(vm, vm->def->disks[i]->src) < 0)
762
            return -1;
763 764
    }

765 766
    rv = virCgroupAllowDevice(priv->cgroup, 'c', DEVICE_PTY_MAJOR, -1,
                              VIR_CGROUP_DEVICE_RW);
767
    virDomainAuditCgroupMajor(vm, priv->cgroup, "allow", DEVICE_PTY_MAJOR,
768 769
                              "pty", "rw", rv == 0);
    if (rv < 0)
770
        return -1;
771 772 773 774 775 776

    deviceACL = cfg->cgroupDeviceACL ?
                (const char *const *)cfg->cgroupDeviceACL :
                defaultDeviceACL;

    if (vm->def->nsounds &&
777
        ((!vm->def->ngraphics && cfg->nogfxAllowHostAudio) ||
778 779
         (vm->def->graphics &&
          ((vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
780
           cfg->vncAllowHostAudio) ||
781
           (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL))))) {
782 783
        rv = virCgroupAllowDevice(priv->cgroup, 'c', DEVICE_SND_MAJOR, -1,
                                  VIR_CGROUP_DEVICE_RW);
784
        virDomainAuditCgroupMajor(vm, priv->cgroup, "allow", DEVICE_SND_MAJOR,
785 786
                                  "sound", "rw", rv == 0);
        if (rv < 0)
787
            return -1;
788 789
    }

790
    for (i = 0; deviceACL[i] != NULL; i++) {
791
        if (!virFileExists(deviceACL[i])) {
N
Nehal J Wani 已提交
792
            VIR_DEBUG("Ignoring non-existent device %s", deviceACL[i]);
793 794 795
            continue;
        }

796
        rv = virCgroupAllowDevicePath(priv->cgroup, deviceACL[i],
797
                                      VIR_CGROUP_DEVICE_RW, false);
798
        virDomainAuditCgroupPath(vm, priv->cgroup, "allow", deviceACL[i], "rw", rv);
799 800
        if (rv < 0 &&
            !virLastErrorIsSystemErrno(ENOENT))
801
            return -1;
802 803 804 805
    }

    if (virDomainChrDefForeach(vm->def,
                               true,
806
                               qemuSetupChardevCgroupCB,
807
                               vm) < 0)
808
        return -1;
809

810
    if (vm->def->tpm && qemuSetupTPMCgroup(vm) < 0)
811
        return -1;
812 813

    for (i = 0; i < vm->def->nhostdevs; i++) {
814 815
        /* This may allow /dev/vfio/vfio multiple times, but that
         * is not a problem. Kernel will have only one record. */
816
        if (qemuSetupHostdevCgroup(vm, vm->def->hostdevs[i]) < 0)
817
            return -1;
818 819
    }

820 821
    for (i = 0; i < vm->def->nmems; i++) {
        if (qemuSetupMemoryDevicesCgroup(vm, vm->def->mems[i]) < 0)
822
            return -1;
823 824
    }

825 826
    for (i = 0; i < vm->def->ngraphics; i++) {
        if (qemuSetupGraphicsCgroup(vm, vm->def->graphics[i]) < 0)
827
            return -1;
828 829 830 831
    }

    for (i = 0; i < vm->def->nvideos; i++) {
        if (qemuSetupVideoCgroup(vm, vm->def->videos[i]) < 0)
832
            return -1;
833 834
    }

835 836
    for (i = 0; i < vm->def->ninputs; i++) {
        if (qemuSetupInputCgroup(vm, vm->def->inputs[i]) < 0)
837
            return -1;
838 839
    }

840
    for (i = 0; i < vm->def->nrngs; i++) {
841
        if (qemuSetupRNGCgroup(vm, vm->def->rngs[i]) < 0)
842
            return -1;
843 844
    }

845
    if (vm->def->sev && qemuSetupSEVCgroup(vm) < 0)
846
        return -1;
847

848
    return 0;
849 850 851
}


852
static int
853
qemuSetupCpusetCgroup(virDomainObjPtr vm)
854 855 856 857 858 859
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
        return 0;

860 861 862
    if (virCgroupSetCpusetMemoryMigrate(priv->cgroup, true) < 0)
        return -1;

863
    return 0;
864 865 866
}


867
static int
868
qemuSetupCpuCgroup(virDomainObjPtr vm)
869 870
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
871 872 873 874
    virObjectEventPtr event = NULL;
    virTypedParameterPtr eventParams = NULL;
    int eventNparams = 0;
    int eventMaxparams = 0;
875 876

    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU)) {
877
       if (vm->def->cputune.sharesSpecified) {
878 879 880 881 882 883 884 885
           virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                          _("CPU tuning is not available on this host"));
           return -1;
       } else {
           return 0;
       }
    }

886 887
    if (vm->def->cputune.sharesSpecified) {
        unsigned long long val;
888 889
        if (virCgroupSetupCpuShares(priv->cgroup, vm->def->cputune.shares,
                                    &val) < 0)
890 891
            return -1;

892 893 894 895
        if (vm->def->cputune.shares != val) {
            vm->def->cputune.shares = val;
            if (virTypedParamsAddULLong(&eventParams, &eventNparams,
                                        &eventMaxparams,
896
                                        VIR_DOMAIN_TUNABLE_CPU_CPU_SHARES,
897 898 899 900 901 902
                                        val) < 0)
                return -1;

            event = virDomainEventTunableNewFromObj(vm, eventParams, eventNparams);
        }

903
        virObjectEventStateQueue(priv->driver->domainEventState, event);
904
    }
905 906 907 908 909

    return 0;
}


910
static int
911
qemuInitCgroup(virDomainObjPtr vm,
912 913
               size_t nnicindexes,
               int *nicindexes)
914 915
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
916
    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
917

918
    if (!virQEMUDriverIsPrivileged(priv->driver))
919
        return 0;
920

921
    if (!virCgroupAvailable())
922
        return 0;
923

924
    virCgroupFree(&priv->cgroup);
925

926
    if (!vm->def->resource) {
927 928
        virDomainResourceDefPtr res;

929
        if (VIR_ALLOC(res) < 0)
930
            return -1;
931

932
        res->partition = g_strdup("/machine");
933 934

        vm->def->resource = res;
935 936
    }

937 938 939 940
    if (vm->def->resource->partition[0] != '/') {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Resource partition '%s' must start with '/'"),
                       vm->def->resource->partition);
941
        return -1;
942
    }
943

944
    if (virCgroupNewMachine(priv->machineName,
945 946 947 948 949
                            "qemu",
                            vm->def->uuid,
                            NULL,
                            vm->pid,
                            false,
950
                            nnicindexes, nicindexes,
951 952
                            vm->def->resource->partition,
                            cfg->cgroupControllers,
953
                            cfg->maxThreadsPerProc,
954
                            &priv->cgroup) < 0) {
955
        if (virCgroupNewIgnoreError())
956
            return 0;
957

958
        return -1;
959
    }
960

961
    return 0;
962
}
963

964 965 966
static void
qemuRestoreCgroupState(virDomainObjPtr vm)
{
967 968
    g_autofree char *mem_mask = NULL;
    g_autofree char *nodeset = NULL;
969 970
    int empty = -1;
    qemuDomainObjPrivatePtr priv = vm->privateData;
971
    size_t i = 0;
972
    g_autoptr(virBitmap) all_nodes = NULL;
973
    virCgroupPtr cgroup_temp = NULL;
974

975 976 977 978
    if (!virNumaIsAvailable() ||
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
        return;

979
    if (!(all_nodes = virNumaGetHostMemoryNodeset()))
980 981 982 983 984 985
        goto error;

    if (!(mem_mask = virBitmapFormat(all_nodes)))
        goto error;

    if ((empty = virCgroupHasEmptyTasks(priv->cgroup,
986
                                        VIR_CGROUP_CONTROLLER_CPUSET)) <= 0)
987 988 989 990 991
        goto error;

    if (virCgroupSetCpusetMems(priv->cgroup, mem_mask) < 0)
        goto error;

992
    for (i = 0; i < virDomainDefGetVcpusMax(vm->def); i++) {
993
        virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(vm->def, i);
994 995 996 997

        if (!vcpu->online)
            continue;

J
John Ferlan 已提交
998 999
        if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_VCPU, i,
                               false, &cgroup_temp) < 0 ||
1000 1001 1002 1003 1004
            virCgroupSetCpusetMemoryMigrate(cgroup_temp, true) < 0 ||
            virCgroupGetCpusetMems(cgroup_temp, &nodeset) < 0 ||
            virCgroupSetCpusetMems(cgroup_temp, nodeset) < 0)
            goto cleanup;

1005
        VIR_FREE(nodeset);
1006
        virCgroupFree(&cgroup_temp);
1007 1008
    }

1009 1010 1011
    for (i = 0; i < vm->def->niothreadids; i++) {
        if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD,
                               vm->def->iothreadids[i]->iothread_id,
J
John Ferlan 已提交
1012
                               false, &cgroup_temp) < 0 ||
1013 1014 1015 1016 1017
            virCgroupSetCpusetMemoryMigrate(cgroup_temp, true) < 0 ||
            virCgroupGetCpusetMems(cgroup_temp, &nodeset) < 0 ||
            virCgroupSetCpusetMems(cgroup_temp, nodeset) < 0)
            goto cleanup;

1018
        VIR_FREE(nodeset);
1019
        virCgroupFree(&cgroup_temp);
1020 1021
    }

J
John Ferlan 已提交
1022 1023
    if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
                           false, &cgroup_temp) < 0 ||
1024 1025 1026 1027 1028
        virCgroupSetCpusetMemoryMigrate(cgroup_temp, true) < 0 ||
        virCgroupGetCpusetMems(cgroup_temp, &nodeset) < 0 ||
        virCgroupSetCpusetMems(cgroup_temp, nodeset) < 0)
        goto cleanup;

1029
 cleanup:
1030
    virCgroupFree(&cgroup_temp);
1031 1032 1033 1034 1035 1036 1037
    return;

 error:
    virResetLastError();
    VIR_DEBUG("Couldn't restore cgroups to meaningful state");
    goto cleanup;
}
1038 1039

int
1040
qemuConnectCgroup(virDomainObjPtr vm)
1041 1042
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
1043
    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
1044

1045
    if (!virQEMUDriverIsPrivileged(priv->driver))
1046
        return 0;
1047 1048

    if (!virCgroupAvailable())
1049
        return 0;
1050

1051
    virCgroupFree(&priv->cgroup);
1052

1053 1054 1055
    if (virCgroupNewDetectMachine(vm->def->name,
                                  "qemu",
                                  vm->pid,
1056
                                  cfg->cgroupControllers,
1057
                                  priv->machineName,
1058
                                  &priv->cgroup) < 0)
1059
        return -1;
1060

1061
    qemuRestoreCgroupState(vm);
1062
    return 0;
1063 1064
}

1065
int
1066
qemuSetupCgroup(virDomainObjPtr vm,
1067 1068
                size_t nnicindexes,
                int *nicindexes)
1069
{
1070
    qemuDomainObjPrivatePtr priv = vm->privateData;
1071

1072 1073 1074 1075 1076 1077
    if (!vm->pid) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Cannot setup cgroups until process is started"));
        return -1;
    }

1078
    if (qemuInitCgroup(vm, nnicindexes, nicindexes) < 0)
1079
        return -1;
1080

1081
    if (!priv->cgroup)
1082
        return 0;
1083

1084
    if (qemuSetupDevicesCgroup(vm) < 0)
1085
        return -1;
1086

1087
    if (qemuSetupBlkioCgroup(vm) < 0)
1088
        return -1;
1089

1090
    if (qemuSetupMemoryCgroup(vm) < 0)
1091
        return -1;
1092

1093
    if (qemuSetupCpuCgroup(vm) < 0)
1094
        return -1;
1095

1096
    if (qemuSetupCpusetCgroup(vm) < 0)
1097
        return -1;
1098

1099
    return 0;
1100 1101
}

1102 1103 1104 1105
int
qemuSetupCgroupVcpuBW(virCgroupPtr cgroup,
                      unsigned long long period,
                      long long quota)
1106
{
1107
    return virCgroupSetupCpuPeriodQuota(cgroup, period, quota);
1108 1109
}

1110

1111
int
1112 1113
qemuSetupCgroupCpusetCpus(virCgroupPtr cgroup,
                          virBitmapPtr cpumask)
1114
{
1115
    return virCgroupSetupCpusetCpus(cgroup, cpumask);
1116 1117
}

1118

1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
int
qemuSetupCgroupForExtDevices(virDomainObjPtr vm,
                             virQEMUDriverPtr driver)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    virCgroupPtr cgroup_temp = NULL;
    int ret = -1;

    if (!qemuExtDevicesHasDevice(vm->def) ||
        priv->cgroup == NULL)
        return 0; /* Not supported, so claim success */

    /*
     * If CPU cgroup controller is not initialized here, then we need
     * neither period nor quota settings.  And if CPUSET controller is
     * not initialized either, then there's nothing to do anyway.
     */
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU) &&
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
        return 0;

    if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
                           false, &cgroup_temp) < 0)
        goto cleanup;

1144
    ret = qemuExtDevicesSetupCgroup(driver, vm, cgroup_temp);
1145 1146

 cleanup:
1147
    virCgroupFree(&cgroup_temp);
1148 1149 1150 1151 1152

    return ret;
}


1153 1154 1155 1156 1157 1158
int
qemuSetupGlobalCpuCgroup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    unsigned long long period = vm->def->cputune.global_period;
    long long quota = vm->def->cputune.global_quota;
1159
    g_autofree char *mem_mask = NULL;
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    virDomainNumatuneMemMode mem_mode;

    if ((period || quota) &&
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("cgroup cpu is required for scheduler tuning"));
        return -1;
    }

    /*
     * If CPU cgroup controller is not initialized here, then we need
     * neither period nor quota settings.  And if CPUSET controller is
     * not initialized either, then there's nothing to do anyway.
     */
    if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU) &&
        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
        return 0;


    if (virDomainNumatuneGetMode(vm->def->numa, -1, &mem_mode) == 0 &&
        mem_mode == VIR_DOMAIN_NUMATUNE_MEM_STRICT &&
        virDomainNumatuneMaybeFormatNodeset(vm->def->numa,
                                            priv->autoNodeset,
                                            &mem_mask, -1) < 0)
1184
        return -1;
1185 1186 1187

    if (period || quota) {
        if (qemuSetupCgroupVcpuBW(priv->cgroup, period, quota) < 0)
1188
            return -1;
1189 1190 1191 1192 1193 1194
    }

    return 0;
}


1195
int
1196
qemuRemoveCgroup(virDomainObjPtr vm)
1197
{
1198
    qemuDomainObjPrivatePtr priv = vm->privateData;
1199

1200
    if (priv->cgroup == NULL)
1201 1202
        return 0; /* Not supported, so claim success */

1203
    if (virCgroupTerminateMachine(priv->machineName) < 0) {
1204 1205 1206 1207
        if (!virCgroupNewIgnoreError())
            VIR_DEBUG("Failed to terminate cgroup for %s", vm->def->name);
    }

1208
    return virCgroupRemove(priv->cgroup);
1209
}
1210 1211 1212 1213 1214 1215 1216 1217


static void
qemuCgroupEmulatorAllNodesDataFree(qemuCgroupEmulatorAllNodesDataPtr data)
{
    if (!data)
        return;

1218
    virCgroupFree(&data->emulatorCgroup);
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
    VIR_FREE(data->emulatorMemMask);
    VIR_FREE(data);
}


/**
 * qemuCgroupEmulatorAllNodesAllow:
 * @cgroup: domain cgroup pointer
 * @retData: filled with structure used to roll back the operation
 *
 * Allows all NUMA nodes for the qemu emulator thread temporarily. This is
 * necessary when hotplugging cpus since it requires memory allocated in the
 * DMA region. Afterwards the operation can be reverted by
1232
 * qemuCgroupEmulatorAllNodesRestore.
1233 1234 1235 1236 1237 1238 1239 1240
 *
 * Returns 0 on success -1 on error
 */
int
qemuCgroupEmulatorAllNodesAllow(virCgroupPtr cgroup,
                                qemuCgroupEmulatorAllNodesDataPtr *retData)
{
    qemuCgroupEmulatorAllNodesDataPtr data = NULL;
1241 1242
    g_autofree char *all_nodes_str = NULL;
    g_autoptr(virBitmap) all_nodes = NULL;
1243 1244 1245 1246 1247 1248
    int ret = -1;

    if (!virNumaIsAvailable() ||
        !virCgroupHasController(cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
        return 0;

1249
    if (!(all_nodes = virNumaGetHostMemoryNodeset()))
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
        goto cleanup;

    if (!(all_nodes_str = virBitmapFormat(all_nodes)))
        goto cleanup;

    if (VIR_ALLOC(data) < 0)
        goto cleanup;

    if (virCgroupNewThread(cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
                           false, &data->emulatorCgroup) < 0)
        goto cleanup;

    if (virCgroupGetCpusetMems(data->emulatorCgroup, &data->emulatorMemMask) < 0 ||
        virCgroupSetCpusetMems(data->emulatorCgroup, all_nodes_str) < 0)
        goto cleanup;

1266
    *retData = g_steal_pointer(&data);
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
    ret = 0;

 cleanup:
    qemuCgroupEmulatorAllNodesDataFree(data);

    return ret;
}


/**
1277
 * qemuCgroupEmulatorAllNodesRestore:
1278 1279 1280 1281 1282 1283
 * @data: data structure created by qemuCgroupEmulatorAllNodesAllow
 *
 * Rolls back the setting done by qemuCgroupEmulatorAllNodesAllow and frees the
 * associated data.
 */
void
1284
qemuCgroupEmulatorAllNodesRestore(qemuCgroupEmulatorAllNodesDataPtr data)
1285 1286 1287 1288 1289 1290
{
    virErrorPtr err;

    if (!data)
        return;

1291
    virErrorPreserveLast(&err);
1292
    virCgroupSetCpusetMems(data->emulatorCgroup, data->emulatorMemMask);
1293
    virErrorRestore(&err);
1294 1295 1296

    qemuCgroupEmulatorAllNodesDataFree(data);
}