1 /*
2 * vircgroupv1.c: methods for cgroups v1 backend
3 *
4 * Copyright (C) 2010-2015,2018 Red Hat, Inc.
5 * Copyright IBM Corp. 2008
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21 #include <config.h>
22
23 #include <unistd.h>
24 #ifdef __linux__
25 # include <mntent.h>
26 # include <sys/stat.h>
27 # include <sys/mount.h>
28 #endif /* __linux__ */
29
30 #include "internal.h"
31
32 #define LIBVIRT_VIRCGROUPPRIV_H_ALLOW
33 #include "vircgrouppriv.h"
34
35 #include "vircgroup.h"
36 #include "vircgroupbackend.h"
37 #include "vircgroupv1.h"
38 #include "virfile.h"
39 #include "virlog.h"
40 #include "virstring.h"
41 #include "virsystemd.h"
42 #include "virerror.h"
43 #include "viralloc.h"
44 #include "virthread.h"
45
46 VIR_LOG_INIT("util.cgroup");
47
48 #define VIR_FROM_THIS VIR_FROM_CGROUP
49
50
51 VIR_ENUM_DECL(virCgroupV1Controller);
52 VIR_ENUM_IMPL(virCgroupV1Controller,
53 VIR_CGROUP_CONTROLLER_LAST,
54 "cpu", "cpuacct", "cpuset", "memory", "devices",
55 "freezer", "blkio", "net_cls", "perf_event",
56 "name=systemd",
57 );
58
59
60 #ifdef __linux__
61
62 /* We're looking for at least one 'cgroup' fs mount,
63 * which is *not* a named mount. */
64 static bool
virCgroupV1Available(void)65 virCgroupV1Available(void)
66 {
67 bool ret = false;
68 FILE *mounts = NULL;
69 struct mntent entry;
70 char buf[CGROUP_MAX_VAL];
71
72 if (!virFileExists("/proc/cgroups"))
73 return false;
74
75 if (!(mounts = fopen("/proc/mounts", "r")))
76 return false;
77
78 while (getmntent_r(mounts, &entry, buf, sizeof(buf)) != NULL) {
79 if (STREQ(entry.mnt_type, "cgroup") && !strstr(entry.mnt_opts, "name=")) {
80 ret = true;
81 break;
82 }
83 }
84
85 VIR_FORCE_FCLOSE(mounts);
86 return ret;
87 }
88
89
90 static bool
virCgroupV1ValidateMachineGroup(virCgroup * group,const char * name,const char * drivername,const char * machinename)91 virCgroupV1ValidateMachineGroup(virCgroup *group,
92 const char *name,
93 const char *drivername,
94 const char *machinename)
95 {
96 size_t i;
97 g_autofree char *partname = NULL;
98 g_autofree char *scopename_old = NULL;
99 g_autofree char *scopename_new = NULL;
100 g_autofree char *partmachinename = NULL;
101
102 partname = g_strdup_printf("%s.libvirt-%s", name, drivername);
103
104 if (virCgroupPartitionEscape(&partname) < 0)
105 return false;
106
107 partmachinename = g_strdup_printf("%s.libvirt-%s",
108 machinename, drivername);
109
110 if (virCgroupPartitionEscape(&partmachinename) < 0)
111 return false;
112
113 if (!(scopename_old = virSystemdMakeScopeName(name, drivername, true)))
114 return false;
115
116 if (!(scopename_new = virSystemdMakeScopeName(machinename,
117 drivername, false)))
118 return false;
119
120 if (virCgroupPartitionEscape(&scopename_old) < 0)
121 return false;
122
123 if (virCgroupPartitionEscape(&scopename_new) < 0)
124 return false;
125
126 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
127 char *tmp;
128
129 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
130 continue;
131
132 if (!group->legacy[i].placement)
133 continue;
134
135 tmp = strrchr(group->legacy[i].placement, '/');
136 if (!tmp)
137 return false;
138
139 if (i == VIR_CGROUP_CONTROLLER_CPU ||
140 i == VIR_CGROUP_CONTROLLER_CPUACCT ||
141 i == VIR_CGROUP_CONTROLLER_CPUSET) {
142 if (STREQ(tmp, "/emulator"))
143 *tmp = '\0';
144 tmp = strrchr(group->legacy[i].placement, '/');
145 if (!tmp)
146 return false;
147 }
148
149 tmp++;
150
151 if (STRNEQ(tmp, name) &&
152 STRNEQ(tmp, machinename) &&
153 STRNEQ(tmp, partname) &&
154 STRNEQ(tmp, partmachinename) &&
155 STRNEQ(tmp, scopename_old) &&
156 STRNEQ(tmp, scopename_new)) {
157 VIR_DEBUG("Name '%s' for controller '%s' does not match "
158 "'%s', '%s', '%s', '%s' or '%s'",
159 tmp, virCgroupV1ControllerTypeToString(i),
160 name, machinename, partname,
161 scopename_old, scopename_new);
162 return false;
163 }
164 }
165
166 return true;
167 }
168
169
170 static int
virCgroupV1CopyMounts(virCgroup * group,virCgroup * parent)171 virCgroupV1CopyMounts(virCgroup *group,
172 virCgroup *parent)
173 {
174 size_t i;
175 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
176 if (!parent->legacy[i].mountPoint)
177 continue;
178
179 group->legacy[i].mountPoint = g_strdup(parent->legacy[i].mountPoint);
180
181 group->legacy[i].linkPoint = g_strdup(parent->legacy[i].linkPoint);
182 }
183 return 0;
184 }
185
186
187 static int
virCgroupV1CopyPlacement(virCgroup * group,const char * path,virCgroup * parent)188 virCgroupV1CopyPlacement(virCgroup *group,
189 const char *path,
190 virCgroup *parent)
191 {
192 size_t i;
193 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
194 bool delim;
195
196 if (!group->legacy[i].mountPoint)
197 continue;
198
199 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
200 continue;
201
202 delim = STREQ(parent->legacy[i].placement, "/") || STREQ(path, "");
203 /*
204 * parent == "/" + path="" => "/"
205 * parent == "/libvirt.service" + path == "" => "/libvirt.service"
206 * parent == "/libvirt.service" + path == "foo" => "/libvirt.service/foo"
207 */
208 group->legacy[i].placement = g_strdup_printf("%s%s%s",
209 parent->legacy[i].placement,
210 delim ? "" : "/",
211 path);
212 }
213
214 return 0;
215 }
216
217
218 static int
virCgroupV1ResolveMountLink(const char * mntDir,const char * typeStr,virCgroupV1Controller * controller)219 virCgroupV1ResolveMountLink(const char *mntDir,
220 const char *typeStr,
221 virCgroupV1Controller *controller)
222 {
223 g_autofree char *linkSrc = NULL;
224 g_autofree char *tmp = NULL;
225 char *dirName;
226 GStatBuf sb;
227
228 tmp = g_strdup(mntDir);
229
230 dirName = strrchr(tmp, '/');
231 if (!dirName) {
232 virReportError(VIR_ERR_INTERNAL_ERROR,
233 _("Missing '/' separator in cgroup mount '%s'"), tmp);
234 return -1;
235 }
236
237 if (!strchr(dirName + 1, ','))
238 return 0;
239
240 *dirName = '\0';
241 linkSrc = g_strdup_printf("%s/%s", tmp, typeStr);
242 *dirName = '/';
243
244 if (g_lstat(linkSrc, &sb) < 0) {
245 if (errno == ENOENT) {
246 VIR_WARN("Controller %s co-mounted at %s is missing symlink at %s",
247 typeStr, tmp, linkSrc);
248 } else {
249 virReportSystemError(errno, _("Cannot stat %s"), linkSrc);
250 return -1;
251 }
252 } else {
253 if (!S_ISLNK(sb.st_mode)) {
254 VIR_WARN("Expecting a symlink at %s for controller %s",
255 linkSrc, typeStr);
256 } else {
257 controller->linkPoint = g_steal_pointer(&linkSrc);
258 }
259 }
260
261 return 0;
262 }
263
264
265 static bool
virCgroupV1MountOptsMatchController(const char * mntOpts,const char * typeStr)266 virCgroupV1MountOptsMatchController(const char *mntOpts,
267 const char *typeStr)
268 {
269 const char *tmp = mntOpts;
270 int typeLen = strlen(typeStr);
271
272 while (tmp) {
273 const char *next = strchr(tmp, ',');
274 int len;
275 if (next) {
276 len = next - tmp;
277 next++;
278 } else {
279 len = strlen(tmp);
280 }
281
282 if (typeLen == len && STREQLEN(typeStr, tmp, len))
283 return true;
284
285 tmp = next;
286 }
287
288 return false;
289 }
290
291
292 static int
virCgroupV1DetectMounts(virCgroup * group,const char * mntType,const char * mntOpts,const char * mntDir)293 virCgroupV1DetectMounts(virCgroup *group,
294 const char *mntType,
295 const char *mntOpts,
296 const char *mntDir)
297 {
298 size_t i;
299
300 if (STRNEQ(mntType, "cgroup"))
301 return 0;
302
303 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
304 const char *typestr = virCgroupV1ControllerTypeToString(i);
305
306 if (virCgroupV1MountOptsMatchController(mntOpts, typestr)) {
307 /* Note that the lines in /proc/mounts have the same
308 * order than the mount operations, and that there may
309 * be duplicates due to bind mounts. This means
310 * that the same mount point may be processed more than
311 * once. We need to save the results of the last one,
312 * and we need to be careful to release the memory used
313 * by previous processing. */
314 virCgroupV1Controller *controller = &group->legacy[i];
315
316 VIR_FREE(controller->mountPoint);
317 VIR_FREE(controller->linkPoint);
318 controller->mountPoint = g_strdup(mntDir);
319
320 /* If it is a co-mount it has a filename like "cpu,cpuacct"
321 * and we must identify the symlink path */
322 if (virCgroupV1ResolveMountLink(mntDir, typestr, controller) < 0)
323 return -1;
324 }
325 }
326
327 return 0;
328 }
329
330
331 static int
virCgroupV1DetectPlacement(virCgroup * group,const char * path,const char * controllers,const char * selfpath)332 virCgroupV1DetectPlacement(virCgroup *group,
333 const char *path,
334 const char *controllers,
335 const char *selfpath)
336 {
337 size_t i;
338
339 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
340 const char *typestr = virCgroupV1ControllerTypeToString(i);
341 g_autofree char* placement = NULL;
342 char *tmp = NULL;
343
344 if (!virCgroupV1MountOptsMatchController(controllers, typestr))
345 continue;
346
347 if (!group->legacy[i].mountPoint)
348 continue;
349
350 if (group->legacy[i].placement)
351 continue;
352
353 /* On systemd we create a nested cgroup for some cgroup tasks
354 * but the placement should point to the root cgroup. */
355 placement = g_strdup(selfpath);
356 tmp = g_strrstr(placement, "/libvirt");
357 if (tmp)
358 *tmp = '\0';
359
360 /*
361 * selfpath == "/" + path="" -> "/"
362 * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
363 * selfpath == "/libvirt.service" + path == "foo" -> "/libvirt.service/foo"
364 */
365 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
366 group->legacy[i].placement = g_strdup(placement);
367 } else {
368 bool delim = STREQ(placement, "/") || STREQ(path, "");
369
370 group->legacy[i].placement = g_strdup_printf("%s%s%s", placement,
371 delim ? "" : "/",
372 path);
373 }
374 }
375
376 return 0;
377 }
378
379
380 static int
virCgroupV1SetPlacement(virCgroup * group,const char * path)381 virCgroupV1SetPlacement(virCgroup *group,
382 const char *path)
383 {
384 size_t i;
385
386 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
387 if (!group->legacy[i].mountPoint)
388 continue;
389
390 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
391 continue;
392
393 group->legacy[i].placement = g_strdup(path);
394 }
395
396 return 0;
397 }
398
399
400 static int
virCgroupV1ValidatePlacement(virCgroup * group,pid_t pid)401 virCgroupV1ValidatePlacement(virCgroup *group,
402 pid_t pid)
403 {
404 size_t i;
405
406 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
407 if (!group->legacy[i].mountPoint)
408 continue;
409
410 if (!group->legacy[i].placement) {
411 virReportError(VIR_ERR_INTERNAL_ERROR,
412 _("Could not find placement for v1 controller %s at %s"),
413 virCgroupV1ControllerTypeToString(i),
414 group->legacy[i].placement);
415 return -1;
416 }
417
418 VIR_DEBUG("Detected mount/mapping %zu:%s at %s in %s for pid %lld",
419 i,
420 virCgroupV1ControllerTypeToString(i),
421 group->legacy[i].mountPoint,
422 group->legacy[i].placement,
423 (long long) pid);
424 }
425
426 return 0;
427 }
428
429
430 static char *
virCgroupV1StealPlacement(virCgroup * group)431 virCgroupV1StealPlacement(virCgroup *group)
432 {
433 return g_steal_pointer(&group->legacy[VIR_CGROUP_CONTROLLER_SYSTEMD].placement);
434 }
435
436
437 static int
virCgroupV1DetectControllers(virCgroup * group,int controllers,virCgroup * parent G_GNUC_UNUSED,int detected)438 virCgroupV1DetectControllers(virCgroup *group,
439 int controllers,
440 virCgroup *parent G_GNUC_UNUSED,
441 int detected)
442 {
443 size_t i;
444 size_t j;
445
446 if (controllers >= 0) {
447 VIR_DEBUG("Filtering controllers %d", controllers);
448 /* First mark requested but non-existing controllers to be ignored */
449 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
450 if (((1 << i) & controllers)) {
451 int type = 1 << i;
452 if (type & detected) {
453 VIR_FREE(group->legacy[i].mountPoint);
454 VIR_FREE(group->legacy[i].placement);
455 }
456 /* Remove non-existent controllers */
457 if (!group->legacy[i].mountPoint) {
458 VIR_DEBUG("Requested controller '%s' not mounted, ignoring",
459 virCgroupV1ControllerTypeToString(i));
460 controllers &= ~(1 << i);
461 }
462 }
463 }
464 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
465 VIR_DEBUG("Controller '%s' wanted=%s, mount='%s'",
466 virCgroupV1ControllerTypeToString(i),
467 (1 << i) & controllers ? "yes" : "no",
468 NULLSTR(group->legacy[i].mountPoint));
469 if (!((1 << i) & controllers) &&
470 group->legacy[i].mountPoint) {
471 /* Check whether a request to disable a controller
472 * clashes with co-mounting of controllers */
473 for (j = 0; j < VIR_CGROUP_CONTROLLER_LAST; j++) {
474 if (j == i)
475 continue;
476 if (!((1 << j) & controllers))
477 continue;
478
479 if (STREQ_NULLABLE(group->legacy[i].mountPoint,
480 group->legacy[j].mountPoint)) {
481 virReportSystemError(EINVAL,
482 _("V1 controller '%s' is not wanted, but '%s' is co-mounted"),
483 virCgroupV1ControllerTypeToString(i),
484 virCgroupV1ControllerTypeToString(j));
485 return -1;
486 }
487 }
488 VIR_FREE(group->legacy[i].mountPoint);
489 VIR_FREE(group->legacy[i].placement);
490 }
491 }
492 } else {
493 VIR_DEBUG("Auto-detecting controllers");
494 controllers = 0;
495 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
496 int type = 1 << i;
497 if (type & detected) {
498 VIR_FREE(group->legacy[i].mountPoint);
499 VIR_FREE(group->legacy[i].placement);
500 }
501 VIR_DEBUG("Controller '%s' present=%s",
502 virCgroupV1ControllerTypeToString(i),
503 group->legacy[i].mountPoint ? "yes" : "no");
504 if (group->legacy[i].mountPoint == NULL)
505 continue;
506 controllers |= (1 << i);
507 }
508 }
509
510 return controllers;
511 }
512
513
514 static bool
virCgroupV1HasController(virCgroup * group,int controller)515 virCgroupV1HasController(virCgroup *group,
516 int controller)
517 {
518 return group->legacy[controller].mountPoint != NULL;
519 }
520
521
522 static int
virCgroupV1GetAnyController(virCgroup * group)523 virCgroupV1GetAnyController(virCgroup *group)
524 {
525 size_t i;
526
527 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
528 /* Reject any controller with a placement
529 * of '/' to avoid doing bad stuff to the root
530 * cgroup
531 */
532 if (group->legacy[i].mountPoint &&
533 group->legacy[i].placement &&
534 STRNEQ(group->legacy[i].placement, "/")) {
535 return i;
536 }
537 }
538
539 return -1;
540 }
541
542
543 static int
virCgroupV1PathOfController(virCgroup * group,int controller,const char * key,char ** path)544 virCgroupV1PathOfController(virCgroup *group,
545 int controller,
546 const char *key,
547 char **path)
548 {
549 if (group->legacy[controller].mountPoint == NULL) {
550 virReportError(VIR_ERR_INTERNAL_ERROR,
551 _("v1 controller '%s' is not mounted"),
552 virCgroupV1ControllerTypeToString(controller));
553 return -1;
554 }
555
556 if (group->legacy[controller].placement == NULL) {
557 virReportError(VIR_ERR_INTERNAL_ERROR,
558 _("v1 controller '%s' is not enabled for group"),
559 virCgroupV1ControllerTypeToString(controller));
560 return -1;
561 }
562
563 *path = g_strdup_printf("%s%s/%s", group->legacy[controller].mountPoint,
564 group->legacy[controller].placement, NULLSTR_EMPTY(key));
565
566 return 0;
567 }
568
569
570 static int
virCgroupV1CpuSetInherit(virCgroup * parent,virCgroup * group)571 virCgroupV1CpuSetInherit(virCgroup *parent,
572 virCgroup *group)
573 {
574 size_t i;
575 const char *inherit_values[] = {
576 "cpuset.cpus",
577 "cpuset.mems",
578 "cpuset.memory_migrate",
579 };
580
581 VIR_DEBUG("Setting up inheritance %s -> %s",
582 parent->legacy[VIR_CGROUP_CONTROLLER_CPUSET].placement,
583 group->legacy[VIR_CGROUP_CONTROLLER_CPUSET].placement);
584 for (i = 0; i < G_N_ELEMENTS(inherit_values); i++) {
585 g_autofree char *value = NULL;
586
587 if (virCgroupGetValueStr(parent,
588 VIR_CGROUP_CONTROLLER_CPUSET,
589 inherit_values[i],
590 &value) < 0)
591 return -1;
592
593 VIR_DEBUG("Inherit %s = %s", inherit_values[i], value);
594
595 if (virCgroupSetValueStr(group,
596 VIR_CGROUP_CONTROLLER_CPUSET,
597 inherit_values[i],
598 value) < 0)
599 return -1;
600 }
601
602 return 0;
603 }
604
605
606 static int
virCgroupV1SetMemoryUseHierarchy(virCgroup * group)607 virCgroupV1SetMemoryUseHierarchy(virCgroup *group)
608 {
609 unsigned long long value;
610 const char *filename = "memory.use_hierarchy";
611
612 if (virCgroupGetValueU64(group,
613 VIR_CGROUP_CONTROLLER_MEMORY,
614 filename, &value) < 0)
615 return -1;
616
617 /* Setting twice causes error, so if already enabled, skip setting */
618 if (value == 1)
619 return 0;
620
621 if (virCgroupSetValueU64(group,
622 VIR_CGROUP_CONTROLLER_MEMORY,
623 filename, 1) < 0)
624 return -1;
625
626 return 0;
627 }
628
629
630 static int
virCgroupV1MakeGroup(virCgroup * parent,virCgroup * group,bool create,pid_t pid G_GNUC_UNUSED,unsigned int flags)631 virCgroupV1MakeGroup(virCgroup *parent,
632 virCgroup *group,
633 bool create,
634 pid_t pid G_GNUC_UNUSED,
635 unsigned int flags)
636 {
637 size_t i;
638
639 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
640 g_autofree char *path = NULL;
641
642 /* We must never mkdir() in systemd's hierarchy */
643 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
644 VIR_DEBUG("Not creating systemd controller group");
645 continue;
646 }
647
648 /* Skip over controllers that aren't mounted */
649 if (!group->legacy[i].mountPoint) {
650 VIR_DEBUG("Skipping unmounted controller %s",
651 virCgroupV1ControllerTypeToString(i));
652 continue;
653 }
654
655 if (virCgroupV1PathOfController(group, i, "", &path) < 0)
656 return -1;
657
658 VIR_DEBUG("Make controller %s", path);
659 if (!virFileExists(path)) {
660 if (!create ||
661 mkdir(path, 0755) < 0) {
662 if (errno == EEXIST)
663 continue;
664 /* With a kernel that doesn't support multi-level directory
665 * for blkio controller, libvirt will fail and disable all
666 * other controllers even though they are available. So
667 * treat blkio as unmounted if mkdir fails. */
668 if (i == VIR_CGROUP_CONTROLLER_BLKIO) {
669 VIR_DEBUG("Ignoring mkdir failure with blkio controller. Kernel probably too old");
670 VIR_FREE(group->legacy[i].mountPoint);
671 continue;
672 } else {
673 virReportSystemError(errno,
674 _("Failed to create v1 controller %s for group"),
675 virCgroupV1ControllerTypeToString(i));
676 return -1;
677 }
678 }
679 if (i == VIR_CGROUP_CONTROLLER_CPUSET &&
680 group->legacy[i].mountPoint != NULL &&
681 virCgroupV1CpuSetInherit(parent, group) < 0) {
682 return -1;
683 }
684 /*
685 * Note that virCgroupV1SetMemoryUseHierarchy should always be
686 * called prior to creating subcgroups and attaching tasks.
687 */
688 if ((flags & VIR_CGROUP_MEM_HIERACHY) &&
689 i == VIR_CGROUP_CONTROLLER_MEMORY &&
690 group->legacy[i].mountPoint != NULL &&
691 virCgroupV1SetMemoryUseHierarchy(group) < 0) {
692 return -1;
693 }
694 }
695 }
696
697 VIR_DEBUG("Done making controllers for group");
698 return 0;
699 }
700
701
702 static bool
virCgroupV1Exists(virCgroup * group)703 virCgroupV1Exists(virCgroup *group)
704 {
705 size_t i;
706
707 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
708 g_autofree char *path = NULL;
709
710 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
711 continue;
712
713 if (!group->legacy[i].mountPoint)
714 continue;
715
716 if (virCgroupV1PathOfController(group, i, "", &path) < 0)
717 return false;
718
719 if (!virFileExists(path)) {
720 return false;
721 }
722 }
723
724 return true;
725 }
726
727
728 static int
virCgroupV1Remove(virCgroup * group)729 virCgroupV1Remove(virCgroup *group)
730 {
731 int rc = 0;
732 size_t i;
733
734 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
735 g_autofree char *grppath = NULL;
736
737 /* Skip over controllers not mounted */
738 if (!group->legacy[i].mountPoint)
739 continue;
740
741 /* We must never rmdir() in systemd's hierarchy */
742 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD)
743 continue;
744
745 /* Don't delete the root group, if we accidentally
746 ended up in it for some reason */
747 if (STREQ(group->legacy[i].placement, "/"))
748 continue;
749
750 if (virCgroupV1PathOfController(group,
751 i,
752 NULL,
753 &grppath) != 0)
754 continue;
755
756 VIR_DEBUG("Removing cgroup %s and all child cgroups", grppath);
757 rc = virCgroupRemoveRecursively(grppath);
758 }
759
760 return rc;
761 }
762
763
764 static int
virCgroupV1AddTask(virCgroup * group,pid_t pid,unsigned int flags)765 virCgroupV1AddTask(virCgroup *group,
766 pid_t pid,
767 unsigned int flags)
768 {
769 size_t i;
770
771 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
772 /* Skip over controllers not mounted */
773 if (!group->legacy[i].mountPoint)
774 continue;
775
776 /* We must never add tasks in systemd's hierarchy
777 * unless we're intentionally trying to move a
778 * task into a systemd machine scope */
779 if (i == VIR_CGROUP_CONTROLLER_SYSTEMD &&
780 !(flags & VIR_CGROUP_TASK_SYSTEMD))
781 continue;
782
783 if (virCgroupSetValueI64(group, i, "tasks", pid) < 0)
784 return -1;
785 }
786
787 return 0;
788 }
789
790
791 static int
virCgroupV1HasEmptyTasks(virCgroup * cgroup,int controller)792 virCgroupV1HasEmptyTasks(virCgroup *cgroup,
793 int controller)
794 {
795 int ret = -1;
796 g_autofree char *content = NULL;
797
798 if (!cgroup)
799 return -1;
800
801 ret = virCgroupGetValueStr(cgroup, controller, "tasks", &content);
802
803 if (ret == 0 && content[0] == '\0')
804 ret = 1;
805
806 return ret;
807 }
808
809
810 static int
virCgroupV1KillRecursive(virCgroup * group,int signum,GHashTable * pids)811 virCgroupV1KillRecursive(virCgroup *group,
812 int signum,
813 GHashTable *pids)
814 {
815 return virCgroupKillRecursiveInternal(group, signum, pids,
816 "tasks", false);
817 }
818
819
820 static char *
virCgroupV1IdentifyRoot(virCgroup * group)821 virCgroupV1IdentifyRoot(virCgroup *group)
822 {
823 char *ret = NULL;
824 size_t i;
825
826 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
827 char *tmp;
828 if (!group->legacy[i].mountPoint)
829 continue;
830 if (!(tmp = strrchr(group->legacy[i].mountPoint, '/'))) {
831 virReportError(VIR_ERR_INTERNAL_ERROR,
832 _("Could not find directory separator in %s"),
833 group->legacy[i].mountPoint);
834 return NULL;
835 }
836
837 ret = g_strndup(group->legacy[i].mountPoint,
838 tmp - group->legacy[i].mountPoint);
839 return ret;
840 }
841
842 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
843 _("Could not find any mounted v1 controllers"));
844 return NULL;
845 }
846
847
848 static int
virCgroupV1BindMount(virCgroup * group,const char * oldroot,const char * mountopts)849 virCgroupV1BindMount(virCgroup *group,
850 const char *oldroot,
851 const char *mountopts)
852 {
853 size_t i;
854 g_autofree char *opts = NULL;
855 g_autofree char *root = NULL;
856
857 if (!(root = virCgroupV1IdentifyRoot(group)))
858 return -1;
859
860 VIR_DEBUG("Mounting cgroups at '%s'", root);
861
862 if (g_mkdir_with_parents(root, 0777) < 0) {
863 virReportSystemError(errno,
864 _("Unable to create directory %s"),
865 root);
866 return -1;
867 }
868
869 opts = g_strdup_printf("mode=755,size=65536%s", mountopts);
870
871 if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC, opts) < 0) {
872 virReportSystemError(errno,
873 _("Failed to mount %s on %s type %s"),
874 "tmpfs", root, "tmpfs");
875 return -1;
876 }
877
878 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
879 if (!group->legacy[i].mountPoint)
880 continue;
881
882 if (!virFileExists(group->legacy[i].mountPoint)) {
883 g_autofree char *src = NULL;
884 src = g_strdup_printf("%s%s", oldroot, group->legacy[i].mountPoint);
885
886 VIR_DEBUG("Create mount point '%s'",
887 group->legacy[i].mountPoint);
888 if (g_mkdir_with_parents(group->legacy[i].mountPoint, 0777) < 0) {
889 virReportSystemError(errno,
890 _("Unable to create directory %s"),
891 group->legacy[i].mountPoint);
892 return -1;
893 }
894
895 if (mount(src, group->legacy[i].mountPoint, "none", MS_BIND,
896 NULL) < 0) {
897 virReportSystemError(errno,
898 _("Failed to bind cgroup '%s' on '%s'"),
899 src, group->legacy[i].mountPoint);
900 return -1;
901 }
902 }
903
904 if (group->legacy[i].linkPoint) {
905 VIR_DEBUG("Link mount point '%s' to '%s'",
906 group->legacy[i].mountPoint,
907 group->legacy[i].linkPoint);
908 if (symlink(group->legacy[i].mountPoint,
909 group->legacy[i].linkPoint) < 0) {
910 virReportSystemError(errno,
911 _("Unable to symlink directory %s to %s"),
912 group->legacy[i].mountPoint,
913 group->legacy[i].linkPoint);
914 return -1;
915 }
916 }
917 }
918
919 return 0;
920 }
921
922
923 static int
virCgroupV1SetOwner(virCgroup * cgroup,uid_t uid,gid_t gid,int controllers)924 virCgroupV1SetOwner(virCgroup *cgroup,
925 uid_t uid,
926 gid_t gid,
927 int controllers)
928 {
929 size_t i;
930 int direrr;
931
932 for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
933 g_autofree char *base = NULL;
934 struct dirent *de;
935 g_autoptr(DIR) dh = NULL;
936
937 if (!((1 << i) & controllers))
938 continue;
939
940 if (!cgroup->legacy[i].mountPoint)
941 continue;
942
943 base = g_strdup_printf("%s%s", cgroup->legacy[i].mountPoint,
944 cgroup->legacy[i].placement);
945
946 if (virDirOpen(&dh, base) < 0)
947 return -1;
948
949 while ((direrr = virDirRead(dh, &de, base)) > 0) {
950 g_autofree char *entry = NULL;
951
952 entry = g_strdup_printf("%s/%s", base, de->d_name);
953
954 if (chown(entry, uid, gid) < 0) {
955 virReportSystemError(errno,
956 _("cannot chown '%s' to (%u, %u)"),
957 entry, uid, gid);
958 return -1;
959 }
960 }
961 if (direrr < 0)
962 return -1;
963
964 if (chown(base, uid, gid) < 0) {
965 virReportSystemError(errno,
966 _("cannot chown '%s' to (%u, %u)"),
967 base, uid, gid);
968 return -1;
969 }
970 }
971
972 return 0;
973 }
974
975
976 static int
virCgroupV1SetBlkioWeight(virCgroup * group,unsigned int weight)977 virCgroupV1SetBlkioWeight(virCgroup *group,
978 unsigned int weight)
979 {
980 g_autofree char *path = NULL;
981
982 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
983 "blkio.bfq.weight", &path) < 0) {
984 return -1;
985 }
986
987 if (!virFileExists(path)) {
988 VIR_FREE(path);
989
990 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
991 "blkio.weight", &path) < 0) {
992 return -1;
993 }
994 }
995
996 if (!virFileExists(path)) {
997 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
998 _("blkio device weight is valid only for bfq or cfq scheduler"));
999 return -1;
1000 }
1001
1002 if (group->unitName) {
1003 GVariant *value = g_variant_new("t", weight);
1004
1005 return virCgroupSetValueDBus(group->unitName, "BlockIOWeight", value);
1006 } else {
1007 g_autofree char *value = g_strdup_printf("%u", weight);
1008
1009 return virCgroupSetValueRaw(path, value);
1010 }
1011 }
1012
1013
1014 static int
virCgroupV1GetBlkioWeight(virCgroup * group,unsigned int * weight)1015 virCgroupV1GetBlkioWeight(virCgroup *group,
1016 unsigned int *weight)
1017 {
1018 g_autofree char *path = NULL;
1019 g_autofree char *value = NULL;
1020
1021 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
1022 "blkio.bfq.weight", &path) < 0) {
1023 return -1;
1024 }
1025
1026 if (!virFileExists(path)) {
1027 VIR_FREE(path);
1028
1029 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
1030 "blkio.weight", &path) < 0) {
1031 return -1;
1032 }
1033 }
1034
1035 if (!virFileExists(path)) {
1036 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
1037 _("blkio device weight is valid only for bfq or cfq scheduler"));
1038 return -1;
1039 }
1040
1041 if (virCgroupGetValueRaw(path, &value) < 0)
1042 return -1;
1043
1044 if (virStrToLong_ui(value, NULL, 10, weight) < 0) {
1045 virReportError(VIR_ERR_INTERNAL_ERROR,
1046 _("Unable to parse '%s' as an integer"),
1047 value);
1048 return -1;
1049 }
1050
1051 return 0;
1052 }
1053
1054
1055 static int
virCgroupV1GetBlkioIoServiced(virCgroup * group,long long * bytes_read,long long * bytes_write,long long * requests_read,long long * requests_write)1056 virCgroupV1GetBlkioIoServiced(virCgroup *group,
1057 long long *bytes_read,
1058 long long *bytes_write,
1059 long long *requests_read,
1060 long long *requests_write)
1061 {
1062 long long stats_val;
1063 g_autofree char *str1 = NULL;
1064 g_autofree char *str2 = NULL;
1065 char *p1 = NULL;
1066 char *p2 = NULL;
1067 size_t i;
1068
1069 const char *value_names[] = {
1070 "Read ",
1071 "Write "
1072 };
1073 long long *bytes_ptrs[] = {
1074 bytes_read,
1075 bytes_write
1076 };
1077 long long *requests_ptrs[] = {
1078 requests_read,
1079 requests_write
1080 };
1081
1082 *bytes_read = 0;
1083 *bytes_write = 0;
1084 *requests_read = 0;
1085 *requests_write = 0;
1086
1087 if (virCgroupGetValueStr(group,
1088 VIR_CGROUP_CONTROLLER_BLKIO,
1089 "blkio.throttle.io_service_bytes", &str1) < 0)
1090 return -1;
1091
1092 if (virCgroupGetValueStr(group,
1093 VIR_CGROUP_CONTROLLER_BLKIO,
1094 "blkio.throttle.io_serviced", &str2) < 0)
1095 return -1;
1096
1097 /* sum up all entries of the same kind, from all devices */
1098 for (i = 0; i < G_N_ELEMENTS(value_names); i++) {
1099 p1 = str1;
1100 p2 = str2;
1101
1102 while ((p1 = strstr(p1, value_names[i]))) {
1103 p1 += strlen(value_names[i]);
1104 if (virStrToLong_ll(p1, &p1, 10, &stats_val) < 0) {
1105 virReportError(VIR_ERR_INTERNAL_ERROR,
1106 _("Cannot parse byte %sstat '%s'"),
1107 value_names[i],
1108 p1);
1109 return -1;
1110 }
1111
1112 if (stats_val < 0 ||
1113 (stats_val > 0 && *bytes_ptrs[i] > (LLONG_MAX - stats_val)))
1114 {
1115 virReportError(VIR_ERR_OVERFLOW,
1116 _("Sum of byte %sstat overflows"),
1117 value_names[i]);
1118 return -1;
1119 }
1120 *bytes_ptrs[i] += stats_val;
1121 }
1122
1123 while ((p2 = strstr(p2, value_names[i]))) {
1124 p2 += strlen(value_names[i]);
1125 if (virStrToLong_ll(p2, &p2, 10, &stats_val) < 0) {
1126 virReportError(VIR_ERR_INTERNAL_ERROR,
1127 _("Cannot parse %srequest stat '%s'"),
1128 value_names[i],
1129 p2);
1130 return -1;
1131 }
1132
1133 if (stats_val < 0 ||
1134 (stats_val > 0 && *requests_ptrs[i] > (LLONG_MAX - stats_val)))
1135 {
1136 virReportError(VIR_ERR_OVERFLOW,
1137 _("Sum of %srequest stat overflows"),
1138 value_names[i]);
1139 return -1;
1140 }
1141 *requests_ptrs[i] += stats_val;
1142 }
1143 }
1144
1145 return 0;
1146 }
1147
1148
1149 static int
virCgroupV1GetBlkioIoDeviceServiced(virCgroup * group,const char * path,long long * bytes_read,long long * bytes_write,long long * requests_read,long long * requests_write)1150 virCgroupV1GetBlkioIoDeviceServiced(virCgroup *group,
1151 const char *path,
1152 long long *bytes_read,
1153 long long *bytes_write,
1154 long long *requests_read,
1155 long long *requests_write)
1156 {
1157 g_autofree char *str1 = NULL;
1158 g_autofree char *str2 = NULL;
1159 g_autofree char *str3 = NULL;
1160 char *p1 = NULL;
1161 char *p2 = NULL;
1162 size_t i;
1163
1164 const char *value_names[] = {
1165 "Read ",
1166 "Write "
1167 };
1168 long long *bytes_ptrs[] = {
1169 bytes_read,
1170 bytes_write
1171 };
1172 long long *requests_ptrs[] = {
1173 requests_read,
1174 requests_write
1175 };
1176
1177 if (virCgroupGetValueStr(group,
1178 VIR_CGROUP_CONTROLLER_BLKIO,
1179 "blkio.throttle.io_service_bytes", &str1) < 0)
1180 return -1;
1181
1182 if (virCgroupGetValueStr(group,
1183 VIR_CGROUP_CONTROLLER_BLKIO,
1184 "blkio.throttle.io_serviced", &str2) < 0)
1185 return -1;
1186
1187 if (!(str3 = virCgroupGetBlockDevString(path)))
1188 return -1;
1189
1190 if (!(p1 = strstr(str1, str3))) {
1191 virReportError(VIR_ERR_INTERNAL_ERROR,
1192 _("Cannot find byte stats for block device '%s'"),
1193 str3);
1194 return -1;
1195 }
1196
1197 if (!(p2 = strstr(str2, str3))) {
1198 virReportError(VIR_ERR_INTERNAL_ERROR,
1199 _("Cannot find request stats for block device '%s'"),
1200 str3);
1201 return -1;
1202 }
1203
1204 for (i = 0; i < G_N_ELEMENTS(value_names); i++) {
1205 if (!(p1 = strstr(p1, value_names[i]))) {
1206 virReportError(VIR_ERR_INTERNAL_ERROR,
1207 _("Cannot find byte %sstats for block device '%s'"),
1208 value_names[i], str3);
1209 return -1;
1210 }
1211
1212 if (virStrToLong_ll(p1 + strlen(value_names[i]), &p1, 10, bytes_ptrs[i]) < 0) {
1213 virReportError(VIR_ERR_INTERNAL_ERROR,
1214 _("Cannot parse %sstat '%s'"),
1215 value_names[i], p1 + strlen(value_names[i]));
1216 return -1;
1217 }
1218
1219 if (!(p2 = strstr(p2, value_names[i]))) {
1220 virReportError(VIR_ERR_INTERNAL_ERROR,
1221 _("Cannot find request %sstats for block device '%s'"),
1222 value_names[i], str3);
1223 return -1;
1224 }
1225
1226 if (virStrToLong_ll(p2 + strlen(value_names[i]), &p2, 10, requests_ptrs[i]) < 0) {
1227 virReportError(VIR_ERR_INTERNAL_ERROR,
1228 _("Cannot parse %sstat '%s'"),
1229 value_names[i], p2 + strlen(value_names[i]));
1230 return -1;
1231 }
1232 }
1233
1234 return 0;
1235 }
1236
1237
1238 static int
virCgroupV1SetBlkioDeviceWeight(virCgroup * group,const char * devPath,unsigned int weight)1239 virCgroupV1SetBlkioDeviceWeight(virCgroup *group,
1240 const char *devPath,
1241 unsigned int weight)
1242 {
1243 g_autofree char *path = NULL;
1244
1245 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
1246 "blkio.weight_device", &path) < 0) {
1247 return -1;
1248 }
1249
1250 if (!virFileExists(path)) {
1251 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
1252 _("blkio device weight is valid only for cfq scheduler"));
1253 return -1;
1254 }
1255
1256 if (group->unitName) {
1257 GVariant *value = NULL;
1258
1259 value = g_variant_new_parsed("[(%s, uint64 %u)]", path, weight);
1260
1261 return virCgroupSetValueDBus(group->unitName, "BlockIODeviceWeight", value);
1262 } else {
1263 g_autofree char *str = NULL;
1264 g_autofree char *blkstr = NULL;
1265
1266 if (!(blkstr = virCgroupGetBlockDevString(devPath)))
1267 return -1;
1268
1269 str = g_strdup_printf("%s%d", blkstr, weight);
1270
1271 return virCgroupSetValueRaw(path, str);
1272 }
1273 }
1274
1275
1276 static int
virCgroupV1GetBlkioDeviceWeight(virCgroup * group,const char * devPath,unsigned int * weight)1277 virCgroupV1GetBlkioDeviceWeight(virCgroup *group,
1278 const char *devPath,
1279 unsigned int *weight)
1280 {
1281 g_autofree char *str = NULL;
1282 g_autofree char *value = NULL;
1283 g_autofree char *path = NULL;
1284
1285 if (virCgroupV1PathOfController(group, VIR_CGROUP_CONTROLLER_BLKIO,
1286 "blkio.weight_device", &path) < 0) {
1287 return -1;
1288 }
1289
1290 if (!virFileExists(path)) {
1291 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
1292 _("blkio device weight is valid only for cfq scheduler"));
1293 return -1;
1294 }
1295
1296 if (virCgroupGetValueRaw(path, &value) < 0)
1297 return -1;
1298
1299 if (virCgroupGetValueForBlkDev(value, devPath, &str) < 0)
1300 return -1;
1301
1302 if (!str) {
1303 *weight = 0;
1304 } else if (virStrToLong_ui(str, NULL, 10, weight) < 0) {
1305 virReportError(VIR_ERR_INTERNAL_ERROR,
1306 _("Unable to parse '%s' as an integer"),
1307 str);
1308 return -1;
1309 }
1310
1311 return 0;
1312 }
1313
1314
1315 static int
virCgroupV1SetBlkioDeviceReadIops(virCgroup * group,const char * path,unsigned int riops)1316 virCgroupV1SetBlkioDeviceReadIops(virCgroup *group,
1317 const char *path,
1318 unsigned int riops)
1319 {
1320 g_autofree char *str = NULL;
1321 g_autofree char *blkstr = NULL;
1322
1323 if (!(blkstr = virCgroupGetBlockDevString(path)))
1324 return -1;
1325
1326 str = g_strdup_printf("%s%u", blkstr, riops);
1327
1328 return virCgroupSetValueStr(group,
1329 VIR_CGROUP_CONTROLLER_BLKIO,
1330 "blkio.throttle.read_iops_device",
1331 str);
1332 }
1333
1334
1335 static int
virCgroupV1GetBlkioDeviceReadIops(virCgroup * group,const char * path,unsigned int * riops)1336 virCgroupV1GetBlkioDeviceReadIops(virCgroup *group,
1337 const char *path,
1338 unsigned int *riops)
1339 {
1340 g_autofree char *str = NULL;
1341 g_autofree char *value = NULL;
1342
1343 if (virCgroupGetValueStr(group,
1344 VIR_CGROUP_CONTROLLER_BLKIO,
1345 "blkio.throttle.read_iops_device",
1346 &value) < 0) {
1347 return -1;
1348 }
1349
1350 if (virCgroupGetValueForBlkDev(value, path, &str) < 0)
1351 return -1;
1352
1353 if (!str) {
1354 *riops = 0;
1355 } else if (virStrToLong_ui(str, NULL, 10, riops) < 0) {
1356 virReportError(VIR_ERR_INTERNAL_ERROR,
1357 _("Unable to parse '%s' as an integer"),
1358 str);
1359 return -1;
1360 }
1361
1362 return 0;
1363 }
1364
1365
1366 static int
virCgroupV1SetBlkioDeviceWriteIops(virCgroup * group,const char * path,unsigned int wiops)1367 virCgroupV1SetBlkioDeviceWriteIops(virCgroup *group,
1368 const char *path,
1369 unsigned int wiops)
1370 {
1371 g_autofree char *str = NULL;
1372 g_autofree char *blkstr = NULL;
1373
1374 if (!(blkstr = virCgroupGetBlockDevString(path)))
1375 return -1;
1376
1377 str = g_strdup_printf("%s%u", blkstr, wiops);
1378
1379 return virCgroupSetValueStr(group,
1380 VIR_CGROUP_CONTROLLER_BLKIO,
1381 "blkio.throttle.write_iops_device",
1382 str);
1383 }
1384
1385
1386 static int
virCgroupV1GetBlkioDeviceWriteIops(virCgroup * group,const char * path,unsigned int * wiops)1387 virCgroupV1GetBlkioDeviceWriteIops(virCgroup *group,
1388 const char *path,
1389 unsigned int *wiops)
1390 {
1391 g_autofree char *str = NULL;
1392 g_autofree char *value = NULL;
1393
1394 if (virCgroupGetValueStr(group,
1395 VIR_CGROUP_CONTROLLER_BLKIO,
1396 "blkio.throttle.write_iops_device",
1397 &value) < 0) {
1398 return -1;
1399 }
1400
1401 if (virCgroupGetValueForBlkDev(value, path, &str) < 0)
1402 return -1;
1403
1404 if (!str) {
1405 *wiops = 0;
1406 } else if (virStrToLong_ui(str, NULL, 10, wiops) < 0) {
1407 virReportError(VIR_ERR_INTERNAL_ERROR,
1408 _("Unable to parse '%s' as an integer"),
1409 str);
1410 return -1;
1411 }
1412
1413 return 0;
1414 }
1415
1416
1417 static int
virCgroupV1SetBlkioDeviceReadBps(virCgroup * group,const char * path,unsigned long long rbps)1418 virCgroupV1SetBlkioDeviceReadBps(virCgroup *group,
1419 const char *path,
1420 unsigned long long rbps)
1421 {
1422 g_autofree char *str = NULL;
1423 g_autofree char *blkstr = NULL;
1424
1425 if (!(blkstr = virCgroupGetBlockDevString(path)))
1426 return -1;
1427
1428 str = g_strdup_printf("%s%llu", blkstr, rbps);
1429
1430 return virCgroupSetValueStr(group,
1431 VIR_CGROUP_CONTROLLER_BLKIO,
1432 "blkio.throttle.read_bps_device",
1433 str);
1434 }
1435
1436
1437 static int
virCgroupV1GetBlkioDeviceReadBps(virCgroup * group,const char * path,unsigned long long * rbps)1438 virCgroupV1GetBlkioDeviceReadBps(virCgroup *group,
1439 const char *path,
1440 unsigned long long *rbps)
1441 {
1442 g_autofree char *str = NULL;
1443 g_autofree char *value = NULL;
1444
1445 if (virCgroupGetValueStr(group,
1446 VIR_CGROUP_CONTROLLER_BLKIO,
1447 "blkio.throttle.read_bps_device",
1448 &value) < 0) {
1449 return -1;
1450 }
1451
1452 if (virCgroupGetValueForBlkDev(value, path, &str) < 0)
1453 return -1;
1454
1455 if (!str) {
1456 *rbps = 0;
1457 } else if (virStrToLong_ull(str, NULL, 10, rbps) < 0) {
1458 virReportError(VIR_ERR_INTERNAL_ERROR,
1459 _("Unable to parse '%s' as an integer"),
1460 str);
1461 return -1;
1462 }
1463
1464 return 0;
1465 }
1466
1467
1468 static int
virCgroupV1SetBlkioDeviceWriteBps(virCgroup * group,const char * path,unsigned long long wbps)1469 virCgroupV1SetBlkioDeviceWriteBps(virCgroup *group,
1470 const char *path,
1471 unsigned long long wbps)
1472 {
1473 g_autofree char *str = NULL;
1474 g_autofree char *blkstr = NULL;
1475
1476 if (!(blkstr = virCgroupGetBlockDevString(path)))
1477 return -1;
1478
1479 str = g_strdup_printf("%s%llu", blkstr, wbps);
1480
1481 return virCgroupSetValueStr(group,
1482 VIR_CGROUP_CONTROLLER_BLKIO,
1483 "blkio.throttle.write_bps_device",
1484 str);
1485 }
1486
1487
1488 static int
virCgroupV1GetBlkioDeviceWriteBps(virCgroup * group,const char * path,unsigned long long * wbps)1489 virCgroupV1GetBlkioDeviceWriteBps(virCgroup *group,
1490 const char *path,
1491 unsigned long long *wbps)
1492 {
1493 g_autofree char *str = NULL;
1494 g_autofree char *value = NULL;
1495
1496 if (virCgroupGetValueStr(group,
1497 VIR_CGROUP_CONTROLLER_BLKIO,
1498 "blkio.throttle.write_bps_device",
1499 &value) < 0) {
1500 return -1;
1501 }
1502
1503 if (virCgroupGetValueForBlkDev(value, path, &str) < 0)
1504 return -1;
1505
1506 if (!str) {
1507 *wbps = 0;
1508 } else if (virStrToLong_ull(str, NULL, 10, wbps) < 0) {
1509 virReportError(VIR_ERR_INTERNAL_ERROR,
1510 _("Unable to parse '%s' as an integer"),
1511 str);
1512 return -1;
1513 }
1514
1515 return 0;
1516 }
1517
1518
1519 /*
1520 * Retrieve the "memory.limit_in_bytes" value from the memory controller
1521 * root dir. This value cannot be modified by userspace and therefore
1522 * is the maximum limit value supported by cgroups on the local system.
1523 * Returns this value scaled to KB or falls back to the original
1524 * VIR_DOMAIN_MEMORY_PARAM_UNLIMITED. Either way, remember the return
1525 * value to avoid unnecessary cgroup filesystem access.
1526 */
1527 static unsigned long long int virCgroupV1MemoryUnlimitedKB;
1528 static virOnceControl virCgroupV1MemoryOnce = VIR_ONCE_CONTROL_INITIALIZER;
1529
1530 static void
virCgroupV1MemoryOnceInit(void)1531 virCgroupV1MemoryOnceInit(void)
1532 {
1533 g_autoptr(virCgroup) group = NULL;
1534 unsigned long long int mem_unlimited = 0ULL;
1535
1536 if (virCgroupNew("/", -1, &group) < 0)
1537 return;
1538
1539 if (!virCgroupV1HasController(group, VIR_CGROUP_CONTROLLER_MEMORY))
1540 return;
1541
1542 ignore_value(virCgroupGetValueU64(group,
1543 VIR_CGROUP_CONTROLLER_MEMORY,
1544 "memory.limit_in_bytes",
1545 &mem_unlimited));
1546
1547 virCgroupV1MemoryUnlimitedKB = mem_unlimited >> 10;
1548 }
1549
1550 static unsigned long long int
virCgroupV1GetMemoryUnlimitedKB(void)1551 virCgroupV1GetMemoryUnlimitedKB(void)
1552 {
1553 if (virOnce(&virCgroupV1MemoryOnce, virCgroupV1MemoryOnceInit) < 0)
1554 VIR_DEBUG("Init failed, will fall back to defaults.");
1555
1556 if (virCgroupV1MemoryUnlimitedKB)
1557 return virCgroupV1MemoryUnlimitedKB;
1558 else
1559 return VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1560 }
1561
1562
1563 static int
virCgroupV1SetMemory(virCgroup * group,unsigned long long kb)1564 virCgroupV1SetMemory(virCgroup *group,
1565 unsigned long long kb)
1566 {
1567 unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1568
1569 if (kb > maxkb) {
1570 virReportError(VIR_ERR_INVALID_ARG,
1571 _("Memory '%llu' must be less than %llu"),
1572 kb, maxkb);
1573 return -1;
1574 }
1575
1576 if (kb == maxkb)
1577 return virCgroupSetValueI64(group,
1578 VIR_CGROUP_CONTROLLER_MEMORY,
1579 "memory.limit_in_bytes",
1580 -1);
1581 else
1582 return virCgroupSetValueU64(group,
1583 VIR_CGROUP_CONTROLLER_MEMORY,
1584 "memory.limit_in_bytes",
1585 kb << 10);
1586 }
1587
1588
1589 static int
virCgroupV1GetMemoryStat(virCgroup * group,unsigned long long * cache,unsigned long long * activeAnon,unsigned long long * inactiveAnon,unsigned long long * activeFile,unsigned long long * inactiveFile,unsigned long long * unevictable)1590 virCgroupV1GetMemoryStat(virCgroup *group,
1591 unsigned long long *cache,
1592 unsigned long long *activeAnon,
1593 unsigned long long *inactiveAnon,
1594 unsigned long long *activeFile,
1595 unsigned long long *inactiveFile,
1596 unsigned long long *unevictable)
1597 {
1598 g_autofree char *stat = NULL;
1599 char *line = NULL;
1600 unsigned long long cacheVal = 0;
1601 unsigned long long activeAnonVal = 0;
1602 unsigned long long inactiveAnonVal = 0;
1603 unsigned long long activeFileVal = 0;
1604 unsigned long long inactiveFileVal = 0;
1605 unsigned long long unevictableVal = 0;
1606
1607 if (virCgroupGetValueStr(group,
1608 VIR_CGROUP_CONTROLLER_MEMORY,
1609 "memory.stat",
1610 &stat) < 0) {
1611 return -1;
1612 }
1613
1614 line = stat;
1615
1616 while (*line) {
1617 char *newLine = strchr(line, '\n');
1618 char *valueStr = strchr(line, ' ');
1619 unsigned long long value;
1620
1621 if (newLine)
1622 *newLine = '\0';
1623
1624 if (!valueStr) {
1625 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1626 _("Cannot parse 'memory.stat' cgroup file."));
1627 return -1;
1628 }
1629 *valueStr = '\0';
1630
1631 if (virStrToLong_ull(valueStr + 1, NULL, 10, &value) < 0)
1632 return -1;
1633
1634 if (STREQ(line, "cache"))
1635 cacheVal = value >> 10;
1636 else if (STREQ(line, "active_anon"))
1637 activeAnonVal = value >> 10;
1638 else if (STREQ(line, "inactive_anon"))
1639 inactiveAnonVal = value >> 10;
1640 else if (STREQ(line, "active_file"))
1641 activeFileVal = value >> 10;
1642 else if (STREQ(line, "inactive_file"))
1643 inactiveFileVal = value >> 10;
1644 else if (STREQ(line, "unevictable"))
1645 unevictableVal = value >> 10;
1646
1647 if (newLine)
1648 line = newLine + 1;
1649 else
1650 break;
1651 }
1652
1653 *cache = cacheVal;
1654 *activeAnon = activeAnonVal;
1655 *inactiveAnon = inactiveAnonVal;
1656 *activeFile = activeFileVal;
1657 *inactiveFile = inactiveFileVal;
1658 *unevictable = unevictableVal;
1659
1660 return 0;
1661 }
1662
1663
1664 static int
virCgroupV1GetMemoryUsage(virCgroup * group,unsigned long * kb)1665 virCgroupV1GetMemoryUsage(virCgroup *group,
1666 unsigned long *kb)
1667 {
1668 long long unsigned int usage_in_bytes;
1669 int ret;
1670 ret = virCgroupGetValueU64(group,
1671 VIR_CGROUP_CONTROLLER_MEMORY,
1672 "memory.usage_in_bytes", &usage_in_bytes);
1673 if (ret == 0)
1674 *kb = (unsigned long) usage_in_bytes >> 10;
1675 return ret;
1676 }
1677
1678
1679 static int
virCgroupV1SetMemoryHardLimit(virCgroup * group,unsigned long long kb)1680 virCgroupV1SetMemoryHardLimit(virCgroup *group,
1681 unsigned long long kb)
1682 {
1683 return virCgroupV1SetMemory(group, kb);
1684 }
1685
1686
1687 static int
virCgroupV1GetMemoryHardLimit(virCgroup * group,unsigned long long * kb)1688 virCgroupV1GetMemoryHardLimit(virCgroup *group,
1689 unsigned long long *kb)
1690 {
1691 long long unsigned int limit_in_bytes;
1692
1693 if (virCgroupGetValueU64(group,
1694 VIR_CGROUP_CONTROLLER_MEMORY,
1695 "memory.limit_in_bytes", &limit_in_bytes) < 0)
1696 return -1;
1697
1698 *kb = limit_in_bytes >> 10;
1699 if (*kb >= virCgroupV1GetMemoryUnlimitedKB())
1700 *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1701
1702 return 0;
1703 }
1704
1705
1706 static int
virCgroupV1SetMemorySoftLimit(virCgroup * group,unsigned long long kb)1707 virCgroupV1SetMemorySoftLimit(virCgroup *group,
1708 unsigned long long kb)
1709 {
1710 unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1711
1712 if (kb > maxkb) {
1713 virReportError(VIR_ERR_INVALID_ARG,
1714 _("Memory '%llu' must be less than %llu"),
1715 kb, maxkb);
1716 return -1;
1717 }
1718
1719 if (kb == maxkb)
1720 return virCgroupSetValueI64(group,
1721 VIR_CGROUP_CONTROLLER_MEMORY,
1722 "memory.soft_limit_in_bytes",
1723 -1);
1724 else
1725 return virCgroupSetValueU64(group,
1726 VIR_CGROUP_CONTROLLER_MEMORY,
1727 "memory.soft_limit_in_bytes",
1728 kb << 10);
1729 }
1730
1731
1732 static int
virCgroupV1GetMemorySoftLimit(virCgroup * group,unsigned long long * kb)1733 virCgroupV1GetMemorySoftLimit(virCgroup *group,
1734 unsigned long long *kb)
1735 {
1736 long long unsigned int limit_in_bytes;
1737
1738 if (virCgroupGetValueU64(group,
1739 VIR_CGROUP_CONTROLLER_MEMORY,
1740 "memory.soft_limit_in_bytes", &limit_in_bytes) < 0)
1741 return -1;
1742
1743 *kb = limit_in_bytes >> 10;
1744 if (*kb >= virCgroupV1GetMemoryUnlimitedKB())
1745 *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1746
1747 return 0;
1748 }
1749
1750
1751 static int
virCgroupV1SetMemSwapHardLimit(virCgroup * group,unsigned long long kb)1752 virCgroupV1SetMemSwapHardLimit(virCgroup *group,
1753 unsigned long long kb)
1754 {
1755 unsigned long long maxkb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1756
1757 if (kb > maxkb) {
1758 virReportError(VIR_ERR_INVALID_ARG,
1759 _("Memory '%llu' must be less than %llu"),
1760 kb, maxkb);
1761 return -1;
1762 }
1763
1764 if (kb == maxkb)
1765 return virCgroupSetValueI64(group,
1766 VIR_CGROUP_CONTROLLER_MEMORY,
1767 "memory.memsw.limit_in_bytes",
1768 -1);
1769 else
1770 return virCgroupSetValueU64(group,
1771 VIR_CGROUP_CONTROLLER_MEMORY,
1772 "memory.memsw.limit_in_bytes",
1773 kb << 10);
1774 }
1775
1776
1777 static int
virCgroupV1GetMemSwapHardLimit(virCgroup * group,unsigned long long * kb)1778 virCgroupV1GetMemSwapHardLimit(virCgroup *group,
1779 unsigned long long *kb)
1780 {
1781 long long unsigned int limit_in_bytes;
1782
1783 if (virCgroupGetValueU64(group,
1784 VIR_CGROUP_CONTROLLER_MEMORY,
1785 "memory.memsw.limit_in_bytes", &limit_in_bytes) < 0)
1786 return -1;
1787
1788 *kb = limit_in_bytes >> 10;
1789 if (*kb >= virCgroupV1GetMemoryUnlimitedKB())
1790 *kb = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
1791
1792 return 0;
1793 }
1794
1795
1796 static int
virCgroupV1GetMemSwapUsage(virCgroup * group,unsigned long long * kb)1797 virCgroupV1GetMemSwapUsage(virCgroup *group,
1798 unsigned long long *kb)
1799 {
1800 long long unsigned int usage_in_bytes;
1801 int ret;
1802 ret = virCgroupGetValueU64(group,
1803 VIR_CGROUP_CONTROLLER_MEMORY,
1804 "memory.memsw.usage_in_bytes", &usage_in_bytes);
1805 if (ret == 0)
1806 *kb = usage_in_bytes >> 10;
1807 return ret;
1808 }
1809
1810
1811 static int
virCgroupV1AllowDevice(virCgroup * group,char type,int major,int minor,int perms)1812 virCgroupV1AllowDevice(virCgroup *group,
1813 char type,
1814 int major,
1815 int minor,
1816 int perms)
1817 {
1818 g_autofree char *devstr = NULL;
1819 g_autofree char *majorstr = NULL;
1820 g_autofree char *minorstr = NULL;
1821
1822 if (major < 0)
1823 majorstr = g_strdup("*");
1824 else
1825 majorstr = g_strdup_printf("%i", major);
1826
1827 if (minor < 0)
1828 minorstr = g_strdup("*");
1829 else
1830 minorstr = g_strdup_printf("%i", minor);
1831
1832 devstr = g_strdup_printf("%c %s:%s %s", type, majorstr, minorstr,
1833 virCgroupGetDevicePermsString(perms));
1834
1835 if (virCgroupSetValueStr(group,
1836 VIR_CGROUP_CONTROLLER_DEVICES,
1837 "devices.allow",
1838 devstr) < 0)
1839 return -1;
1840
1841 return 0;
1842 }
1843
1844
1845 static int
virCgroupV1DenyDevice(virCgroup * group,char type,int major,int minor,int perms)1846 virCgroupV1DenyDevice(virCgroup *group,
1847 char type,
1848 int major,
1849 int minor,
1850 int perms)
1851 {
1852 g_autofree char *devstr = NULL;
1853 g_autofree char *majorstr = NULL;
1854 g_autofree char *minorstr = NULL;
1855
1856 if (major < 0)
1857 majorstr = g_strdup("*");
1858 else
1859 majorstr = g_strdup_printf("%i", major);
1860
1861 if (minor < 0)
1862 minorstr = g_strdup("*");
1863 else
1864 minorstr = g_strdup_printf("%i", minor);
1865
1866 devstr = g_strdup_printf("%c %s:%s %s", type, majorstr, minorstr,
1867 virCgroupGetDevicePermsString(perms));
1868
1869 if (virCgroupSetValueStr(group,
1870 VIR_CGROUP_CONTROLLER_DEVICES,
1871 "devices.deny",
1872 devstr) < 0)
1873 return -1;
1874
1875 return 0;
1876 }
1877
1878
1879 static int
virCgroupV1AllowAllDevices(virCgroup * group,int perms)1880 virCgroupV1AllowAllDevices(virCgroup *group,
1881 int perms)
1882 {
1883 if (virCgroupV1AllowDevice(group, 'b', -1, -1, perms) < 0)
1884 return -1;
1885
1886 if (virCgroupV1AllowDevice(group, 'c', -1, -1, perms) < 0)
1887 return -1;
1888
1889 return 0;
1890 }
1891
1892
1893 static int
virCgroupV1DenyAllDevices(virCgroup * group)1894 virCgroupV1DenyAllDevices(virCgroup *group)
1895 {
1896 return virCgroupSetValueStr(group,
1897 VIR_CGROUP_CONTROLLER_DEVICES,
1898 "devices.deny",
1899 "a");
1900 }
1901
1902
1903 static int
virCgroupV1SetCpuShares(virCgroup * group,unsigned long long shares)1904 virCgroupV1SetCpuShares(virCgroup *group,
1905 unsigned long long shares)
1906 {
1907 if (shares < VIR_CGROUP_CPU_SHARES_MIN ||
1908 shares > VIR_CGROUP_CPU_SHARES_MAX) {
1909 virReportError(VIR_ERR_INVALID_ARG,
1910 _("shares '%llu' must be in range [%llu, %llu]"),
1911 shares,
1912 VIR_CGROUP_CPU_SHARES_MIN,
1913 VIR_CGROUP_CPU_SHARES_MAX);
1914 return -1;
1915 }
1916
1917 if (group->unitName) {
1918 GVariant *value = g_variant_new("t", shares);
1919
1920 return virCgroupSetValueDBus(group->unitName, "CPUShares", value);
1921 } else {
1922 return virCgroupSetValueU64(group,
1923 VIR_CGROUP_CONTROLLER_CPU,
1924 "cpu.shares", shares);
1925 }
1926 }
1927
1928
1929 static int
virCgroupV1GetCpuShares(virCgroup * group,unsigned long long * shares)1930 virCgroupV1GetCpuShares(virCgroup *group,
1931 unsigned long long *shares)
1932 {
1933 return virCgroupGetValueU64(group,
1934 VIR_CGROUP_CONTROLLER_CPU,
1935 "cpu.shares", shares);
1936 }
1937
1938
1939 static int
virCgroupV1SetCpuCfsPeriod(virCgroup * group,unsigned long long cfs_period)1940 virCgroupV1SetCpuCfsPeriod(virCgroup *group,
1941 unsigned long long cfs_period)
1942 {
1943 if (cfs_period < VIR_CGROUP_CPU_PERIOD_MIN ||
1944 cfs_period > VIR_CGROUP_CPU_PERIOD_MAX) {
1945 virReportError(VIR_ERR_INVALID_ARG,
1946 _("cfs_period '%llu' must be in range (%llu, %llu)"),
1947 cfs_period,
1948 VIR_CGROUP_CPU_PERIOD_MIN,
1949 VIR_CGROUP_CPU_PERIOD_MAX);
1950 return -1;
1951 }
1952
1953 return virCgroupSetValueU64(group,
1954 VIR_CGROUP_CONTROLLER_CPU,
1955 "cpu.cfs_period_us", cfs_period);
1956 }
1957
1958
1959 static int
virCgroupV1GetCpuCfsPeriod(virCgroup * group,unsigned long long * cfs_period)1960 virCgroupV1GetCpuCfsPeriod(virCgroup *group,
1961 unsigned long long *cfs_period)
1962 {
1963 return virCgroupGetValueU64(group,
1964 VIR_CGROUP_CONTROLLER_CPU,
1965 "cpu.cfs_period_us", cfs_period);
1966 }
1967
1968
1969 static int
virCgroupV1SetCpuCfsQuota(virCgroup * group,long long cfs_quota)1970 virCgroupV1SetCpuCfsQuota(virCgroup *group,
1971 long long cfs_quota)
1972 {
1973 if (cfs_quota >= 0 &&
1974 (cfs_quota < VIR_CGROUP_CPU_QUOTA_MIN ||
1975 cfs_quota > VIR_CGROUP_CPU_QUOTA_MAX)) {
1976 virReportError(VIR_ERR_INVALID_ARG,
1977 _("cfs_quota '%lld' must be in range (%llu, %llu)"),
1978 cfs_quota,
1979 VIR_CGROUP_CPU_QUOTA_MIN,
1980 VIR_CGROUP_CPU_QUOTA_MAX);
1981 return -1;
1982 }
1983
1984 return virCgroupSetValueI64(group,
1985 VIR_CGROUP_CONTROLLER_CPU,
1986 "cpu.cfs_quota_us", cfs_quota);
1987 }
1988
1989
1990 static int
virCgroupV1GetCpuCfsQuota(virCgroup * group,long long * cfs_quota)1991 virCgroupV1GetCpuCfsQuota(virCgroup *group,
1992 long long *cfs_quota)
1993 {
1994 return virCgroupGetValueI64(group,
1995 VIR_CGROUP_CONTROLLER_CPU,
1996 "cpu.cfs_quota_us", cfs_quota);
1997 }
1998
1999
2000 static bool
virCgroupV1SupportsCpuBW(virCgroup * cgroup)2001 virCgroupV1SupportsCpuBW(virCgroup *cgroup)
2002 {
2003 g_autofree char *path = NULL;
2004
2005 if (!cgroup)
2006 return false;
2007
2008 if (virCgroupV1PathOfController(cgroup, VIR_CGROUP_CONTROLLER_CPU,
2009 "cpu.cfs_period_us", &path) < 0) {
2010 virResetLastError();
2011 return false;
2012 }
2013
2014 return virFileExists(path);
2015 }
2016
2017
2018 static int
virCgroupV1GetCpuacctUsage(virCgroup * group,unsigned long long * usage)2019 virCgroupV1GetCpuacctUsage(virCgroup *group,
2020 unsigned long long *usage)
2021 {
2022 return virCgroupGetValueU64(group,
2023 VIR_CGROUP_CONTROLLER_CPUACCT,
2024 "cpuacct.usage", usage);
2025 }
2026
2027
2028 static int
virCgroupV1GetCpuacctPercpuUsage(virCgroup * group,char ** usage)2029 virCgroupV1GetCpuacctPercpuUsage(virCgroup *group,
2030 char **usage)
2031 {
2032 return virCgroupGetValueStr(group, VIR_CGROUP_CONTROLLER_CPUACCT,
2033 "cpuacct.usage_percpu", usage);
2034 }
2035
2036
2037 static int
virCgroupV1GetCpuacctStat(virCgroup * group,unsigned long long * user,unsigned long long * sys)2038 virCgroupV1GetCpuacctStat(virCgroup *group,
2039 unsigned long long *user,
2040 unsigned long long *sys)
2041 {
2042 g_autofree char *str = NULL;
2043 char *p;
2044 static double scale = -1.0;
2045
2046 if (virCgroupGetValueStr(group, VIR_CGROUP_CONTROLLER_CPUACCT,
2047 "cpuacct.stat", &str) < 0)
2048 return -1;
2049
2050 if (!(p = STRSKIP(str, "user ")) ||
2051 virStrToLong_ull(p, &p, 10, user) < 0) {
2052 virReportError(VIR_ERR_INTERNAL_ERROR,
2053 _("Cannot parse user stat '%s'"),
2054 p);
2055 return -1;
2056 }
2057 if (!(p = STRSKIP(p, "\nsystem ")) ||
2058 virStrToLong_ull(p, NULL, 10, sys) < 0) {
2059 virReportError(VIR_ERR_INTERNAL_ERROR,
2060 _("Cannot parse sys stat '%s'"),
2061 p);
2062 return -1;
2063 }
2064 /* times reported are in system ticks (generally 100 Hz), but that
2065 * rate can theoretically vary between machines. Scale things
2066 * into approximate nanoseconds. */
2067 if (scale < 0) {
2068 long ticks_per_sec = sysconf(_SC_CLK_TCK);
2069 if (ticks_per_sec == -1) {
2070 virReportSystemError(errno, "%s",
2071 _("Cannot determine system clock HZ"));
2072 return -1;
2073 }
2074 scale = 1000000000.0 / ticks_per_sec;
2075 }
2076 *user *= scale;
2077 *sys *= scale;
2078
2079 return 0;
2080 }
2081
2082
2083 static int
virCgroupV1SetFreezerState(virCgroup * group,const char * state)2084 virCgroupV1SetFreezerState(virCgroup *group,
2085 const char *state)
2086 {
2087 return virCgroupSetValueStr(group,
2088 VIR_CGROUP_CONTROLLER_FREEZER,
2089 "freezer.state", state);
2090 }
2091
2092
2093 static int
virCgroupV1GetFreezerState(virCgroup * group,char ** state)2094 virCgroupV1GetFreezerState(virCgroup *group,
2095 char **state)
2096 {
2097 return virCgroupGetValueStr(group,
2098 VIR_CGROUP_CONTROLLER_FREEZER,
2099 "freezer.state", state);
2100 }
2101
2102
2103 static int
virCgroupV1SetCpusetMems(virCgroup * group,const char * mems)2104 virCgroupV1SetCpusetMems(virCgroup *group,
2105 const char *mems)
2106 {
2107 return virCgroupSetValueStr(group,
2108 VIR_CGROUP_CONTROLLER_CPUSET,
2109 "cpuset.mems",
2110 mems);
2111 }
2112
2113
2114 static int
virCgroupV1GetCpusetMems(virCgroup * group,char ** mems)2115 virCgroupV1GetCpusetMems(virCgroup *group,
2116 char **mems)
2117 {
2118 return virCgroupGetValueStr(group,
2119 VIR_CGROUP_CONTROLLER_CPUSET,
2120 "cpuset.mems",
2121 mems);
2122 }
2123
2124
2125 static int
virCgroupV1SetCpusetMemoryMigrate(virCgroup * group,bool migrate)2126 virCgroupV1SetCpusetMemoryMigrate(virCgroup *group,
2127 bool migrate)
2128 {
2129 return virCgroupSetValueStr(group,
2130 VIR_CGROUP_CONTROLLER_CPUSET,
2131 "cpuset.memory_migrate",
2132 migrate ? "1" : "0");
2133 }
2134
2135
2136 static int
virCgroupV1GetCpusetMemoryMigrate(virCgroup * group,bool * migrate)2137 virCgroupV1GetCpusetMemoryMigrate(virCgroup *group,
2138 bool *migrate)
2139 {
2140 unsigned long long value = 0;
2141 int ret = virCgroupGetValueU64(group,
2142 VIR_CGROUP_CONTROLLER_CPUSET,
2143 "cpuset.memory_migrate",
2144 &value);
2145 *migrate = !!value;
2146 return ret;
2147 }
2148
2149
2150 static int
virCgroupV1SetCpusetCpus(virCgroup * group,const char * cpus)2151 virCgroupV1SetCpusetCpus(virCgroup *group,
2152 const char *cpus)
2153 {
2154 return virCgroupSetValueStr(group,
2155 VIR_CGROUP_CONTROLLER_CPUSET,
2156 "cpuset.cpus",
2157 cpus);
2158 }
2159
2160
2161 static int
virCgroupV1GetCpusetCpus(virCgroup * group,char ** cpus)2162 virCgroupV1GetCpusetCpus(virCgroup *group,
2163 char **cpus)
2164 {
2165 return virCgroupGetValueStr(group,
2166 VIR_CGROUP_CONTROLLER_CPUSET,
2167 "cpuset.cpus",
2168 cpus);
2169 }
2170
2171
2172 virCgroupBackend virCgroupV1Backend = {
2173 .type = VIR_CGROUP_BACKEND_TYPE_V1,
2174
2175 .available = virCgroupV1Available,
2176 .validateMachineGroup = virCgroupV1ValidateMachineGroup,
2177 .copyMounts = virCgroupV1CopyMounts,
2178 .copyPlacement = virCgroupV1CopyPlacement,
2179 .detectMounts = virCgroupV1DetectMounts,
2180 .detectPlacement = virCgroupV1DetectPlacement,
2181 .setPlacement = virCgroupV1SetPlacement,
2182 .validatePlacement = virCgroupV1ValidatePlacement,
2183 .stealPlacement = virCgroupV1StealPlacement,
2184 .detectControllers = virCgroupV1DetectControllers,
2185 .hasController = virCgroupV1HasController,
2186 .getAnyController = virCgroupV1GetAnyController,
2187 .pathOfController = virCgroupV1PathOfController,
2188 .makeGroup = virCgroupV1MakeGroup,
2189 .exists = virCgroupV1Exists,
2190 .remove = virCgroupV1Remove,
2191 .addTask = virCgroupV1AddTask,
2192 .hasEmptyTasks = virCgroupV1HasEmptyTasks,
2193 .killRecursive = virCgroupV1KillRecursive,
2194 .bindMount = virCgroupV1BindMount,
2195 .setOwner = virCgroupV1SetOwner,
2196
2197 .setBlkioWeight = virCgroupV1SetBlkioWeight,
2198 .getBlkioWeight = virCgroupV1GetBlkioWeight,
2199 .getBlkioIoServiced = virCgroupV1GetBlkioIoServiced,
2200 .getBlkioIoDeviceServiced = virCgroupV1GetBlkioIoDeviceServiced,
2201 .setBlkioDeviceWeight = virCgroupV1SetBlkioDeviceWeight,
2202 .getBlkioDeviceWeight = virCgroupV1GetBlkioDeviceWeight,
2203 .setBlkioDeviceReadIops = virCgroupV1SetBlkioDeviceReadIops,
2204 .getBlkioDeviceReadIops = virCgroupV1GetBlkioDeviceReadIops,
2205 .setBlkioDeviceWriteIops = virCgroupV1SetBlkioDeviceWriteIops,
2206 .getBlkioDeviceWriteIops = virCgroupV1GetBlkioDeviceWriteIops,
2207 .setBlkioDeviceReadBps = virCgroupV1SetBlkioDeviceReadBps,
2208 .getBlkioDeviceReadBps = virCgroupV1GetBlkioDeviceReadBps,
2209 .setBlkioDeviceWriteBps = virCgroupV1SetBlkioDeviceWriteBps,
2210 .getBlkioDeviceWriteBps = virCgroupV1GetBlkioDeviceWriteBps,
2211
2212 .setMemory = virCgroupV1SetMemory,
2213 .getMemoryStat = virCgroupV1GetMemoryStat,
2214 .getMemoryUsage = virCgroupV1GetMemoryUsage,
2215 .setMemoryHardLimit = virCgroupV1SetMemoryHardLimit,
2216 .getMemoryHardLimit = virCgroupV1GetMemoryHardLimit,
2217 .setMemorySoftLimit = virCgroupV1SetMemorySoftLimit,
2218 .getMemorySoftLimit = virCgroupV1GetMemorySoftLimit,
2219 .setMemSwapHardLimit = virCgroupV1SetMemSwapHardLimit,
2220 .getMemSwapHardLimit = virCgroupV1GetMemSwapHardLimit,
2221 .getMemSwapUsage = virCgroupV1GetMemSwapUsage,
2222
2223 .allowDevice = virCgroupV1AllowDevice,
2224 .denyDevice = virCgroupV1DenyDevice,
2225 .allowAllDevices = virCgroupV1AllowAllDevices,
2226 .denyAllDevices = virCgroupV1DenyAllDevices,
2227
2228 .setCpuShares = virCgroupV1SetCpuShares,
2229 .getCpuShares = virCgroupV1GetCpuShares,
2230 .setCpuCfsPeriod = virCgroupV1SetCpuCfsPeriod,
2231 .getCpuCfsPeriod = virCgroupV1GetCpuCfsPeriod,
2232 .setCpuCfsQuota = virCgroupV1SetCpuCfsQuota,
2233 .getCpuCfsQuota = virCgroupV1GetCpuCfsQuota,
2234 .supportsCpuBW = virCgroupV1SupportsCpuBW,
2235
2236 .getCpuacctUsage = virCgroupV1GetCpuacctUsage,
2237 .getCpuacctPercpuUsage = virCgroupV1GetCpuacctPercpuUsage,
2238 .getCpuacctStat = virCgroupV1GetCpuacctStat,
2239
2240 .setFreezerState = virCgroupV1SetFreezerState,
2241 .getFreezerState = virCgroupV1GetFreezerState,
2242
2243 .setCpusetMems = virCgroupV1SetCpusetMems,
2244 .getCpusetMems = virCgroupV1GetCpusetMems,
2245 .setCpusetMemoryMigrate = virCgroupV1SetCpusetMemoryMigrate,
2246 .getCpusetMemoryMigrate = virCgroupV1GetCpusetMemoryMigrate,
2247 .setCpusetCpus = virCgroupV1SetCpusetCpus,
2248 .getCpusetCpus = virCgroupV1GetCpusetCpus,
2249 };
2250
2251
2252 void
virCgroupV1Register(void)2253 virCgroupV1Register(void)
2254 {
2255 virCgroupBackendRegister(&virCgroupV1Backend);
2256 }
2257
2258 #else /* !__linux__ */
2259
2260 void
virCgroupV1Register(void)2261 virCgroupV1Register(void)
2262 {
2263 VIR_INFO("Control groups not supported on this platform");
2264 }
2265
2266 #endif /* !__linux__ */
2267