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