1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
25 #include <sys/types.h>
27 #include <pakfire/cgroup.h>
28 #include <pakfire/logging.h>
29 #include <pakfire/pakfire.h>
30 #include <pakfire/string.h>
31 #include <pakfire/util.h>
33 #define ROOT "/sys/fs/cgroup"
34 #define BUFFER_SIZE 64 * 1024
36 enum pakfire_cgroup_controllers
{
37 PAKFIRE_CGROUP_CONTROLLER_CPU
= (1 << 0),
38 PAKFIRE_CGROUP_CONTROLLER_MEMORY
= (1 << 1),
39 PAKFIRE_CGROUP_CONTROLLER_PIDS
= (1 << 2),
40 PAKFIRE_CGROUP_CONTROLLER_IO
= (1 << 3),
43 static const enum pakfire_cgroup_controllers pakfire_cgroup_accounting_controllers
=
44 PAKFIRE_CGROUP_CONTROLLER_CPU
|
45 PAKFIRE_CGROUP_CONTROLLER_MEMORY
|
46 PAKFIRE_CGROUP_CONTROLLER_PIDS
|
47 PAKFIRE_CGROUP_CONTROLLER_IO
;
49 struct pakfire_cgroup
{
50 struct pakfire
* pakfire
;
59 // File descriptor to cgroup
63 // Returns true if this is the root cgroup
64 static int pakfire_cgroup_is_root(struct pakfire_cgroup
* cgroup
) {
65 return !*cgroup
->path
;
68 static int pakfire_cgroup_has_flag(struct pakfire_cgroup
* cgroup
, int flag
) {
69 return cgroup
->flags
& flag
;
72 static const char* pakfire_cgroup_name(struct pakfire_cgroup
* cgroup
) {
73 if (pakfire_cgroup_is_root(cgroup
))
79 static const char* pakfire_cgroup_controller_name(
80 enum pakfire_cgroup_controllers controller
) {
82 case PAKFIRE_CGROUP_CONTROLLER_CPU
:
85 case PAKFIRE_CGROUP_CONTROLLER_MEMORY
:
88 case PAKFIRE_CGROUP_CONTROLLER_PIDS
:
91 case PAKFIRE_CGROUP_CONTROLLER_IO
:
98 static enum pakfire_cgroup_controllers
pakfire_cgroup_find_controller_by_name(
100 const char* n
= NULL
;
102 // Walk through the bitmap
103 for (unsigned int i
= 1; i
; i
<<= 1) {
104 n
= pakfire_cgroup_controller_name(i
);
109 if (strcmp(name
, n
) == 0)
117 static struct pakfire_cgroup
* pakfire_cgroup_parent(struct pakfire_cgroup
* cgroup
) {
118 struct pakfire_cgroup
* parent
= NULL
;
121 // Cannot return parent for root group
122 if (pakfire_cgroup_is_root(cgroup
))
125 // Determine the path of the parent
126 const char* path
= pakfire_dirname(cgroup
->path
);
128 ERROR(cgroup
->pakfire
, "Could not determine path for parent cgroup: %m\n");
132 // dirname() returns . if no directory component could be found
133 if (strcmp(path
, ".") == 0)
137 r
= pakfire_cgroup_open(&parent
, cgroup
->pakfire
, path
, 0);
139 ERROR(cgroup
->pakfire
, "Could not open parent cgroup: %m\n");
146 static void pakfire_cgroup_free(struct pakfire_cgroup
* cgroup
) {
147 DEBUG(cgroup
->pakfire
, "Releasing cgroup %s at %p\n",
148 pakfire_cgroup_name(cgroup
), cgroup
);
150 // Close the file descriptor
154 pakfire_unref(cgroup
->pakfire
);
158 static int pakfire_cgroup_open_root(struct pakfire_cgroup
* cgroup
) {
159 int fd
= open(ROOT
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
);
161 ERROR(cgroup
->pakfire
, "Could not open %s: %m\n", ROOT
);
168 static int __pakfire_cgroup_create(struct pakfire_cgroup
* cgroup
) {
172 DEBUG(cgroup
->pakfire
, "Trying to create cgroup %s\n", pakfire_cgroup_name(cgroup
));
174 // Compose the absolute path
175 r
= pakfire_path_join(path
, ROOT
, cgroup
->path
);
179 // Try creating the directory
180 return pakfire_mkdir(path
, 0755);
184 Opens the cgroup and returns a file descriptor.
186 If the cgroup does not exist, it will try to create it.
188 This function returns a negative value on error.
190 static int __pakfire_cgroup_open(struct pakfire_cgroup
* cgroup
) {
194 // Open file descriptor of the cgroup root
195 int rootfd
= pakfire_cgroup_open_root(cgroup
);
199 // Return the rootfd for the root group
200 if (pakfire_cgroup_is_root(cgroup
))
204 fd
= openat(rootfd
, cgroup
->path
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
);
207 // If the cgroup doesn't exist yet, try to create it
209 r
= __pakfire_cgroup_create(cgroup
);
213 // Retry open after successful creation
216 // Exit on all other errors
218 ERROR(cgroup
->pakfire
, "Could not open cgroup %s: %m\n",
219 pakfire_cgroup_name(cgroup
));
231 static int pakfire_cgroup_access(struct pakfire_cgroup
* cgroup
, const char* path
,
232 int mode
, int flags
) {
233 return faccessat(cgroup
->fd
, path
, mode
, flags
);
236 static FILE* pakfire_cgroup_open_file(struct pakfire_cgroup
* cgroup
,
237 const char* path
, const char* mode
) {
241 int fd
= openat(cgroup
->fd
, "cgroup.procs", O_CLOEXEC
);
243 ERROR(cgroup
->pakfire
, "%s: Could not open %s: %m\n",
244 pakfire_cgroup_name(cgroup
), path
);
248 // Convert into file handle
249 f
= fdopen(fd
, mode
);
260 static ssize_t
pakfire_cgroup_read(struct pakfire_cgroup
* cgroup
, const char* path
,
261 char* buffer
, size_t length
) {
262 ssize_t bytes_read
= -1;
264 // Check if this cgroup has been destroyed already
266 ERROR(cgroup
->pakfire
, "Trying to read from destroyed cgroup\n");
271 int fd
= openat(cgroup
->fd
, path
, O_CLOEXEC
);
273 DEBUG(cgroup
->pakfire
, "Could not open %s/%s: %m\n",
274 pakfire_cgroup_name(cgroup
), path
);
278 // Read file content into buffer
279 bytes_read
= read(fd
, buffer
, length
);
280 if (bytes_read
< 0) {
281 DEBUG(cgroup
->pakfire
, "Could not read from %s/%s: %m\n",
282 pakfire_cgroup_name(cgroup
), path
);
286 // Terminate the buffer
287 if ((size_t)bytes_read
< length
)
288 buffer
[bytes_read
] = '\0';
297 static int pakfire_cgroup_write(struct pakfire_cgroup
* cgroup
,
298 const char* path
, const char* format
, ...) {
302 // Check if this cgroup has been destroyed already
304 ERROR(cgroup
->pakfire
, "Trying to write to destroyed cgroup\n");
310 int fd
= openat(cgroup
->fd
, path
, O_WRONLY
|O_CLOEXEC
);
312 DEBUG(cgroup
->pakfire
, "Could not open %s/%s for writing: %m\n",
313 pakfire_cgroup_name(cgroup
), path
);
318 va_start(args
, format
);
319 ssize_t bytes_written
= vdprintf(fd
, format
, args
);
322 // Check if content was written okay
323 if (bytes_written
< 0) {
324 DEBUG(cgroup
->pakfire
, "Could not write to %s/%s: %m\n",
325 pakfire_cgroup_name(cgroup
), path
);
335 static int pakfire_cgroup_read_controllers(
336 struct pakfire_cgroup
* cgroup
, const char* name
) {
337 char buffer
[BUFFER_SIZE
];
340 // Discovered controllers
343 // Read cgroup.controllers file
344 ssize_t bytes_read
= pakfire_cgroup_read(cgroup
, name
, buffer
, sizeof(buffer
));
348 // If the file was empty, there is nothing more to do
352 char* token
= strtok_r(buffer
, " \n", &p
);
355 DEBUG(cgroup
->pakfire
, "Found controller '%s'\n", token
);
357 // Try finding this controller
358 int controller
= pakfire_cgroup_find_controller_by_name(token
);
360 controllers
|= controller
;
362 // Move on to next token
363 token
= strtok_r(NULL
, " \n", &p
);
366 // Return discovered controllers
371 Returns a bitmap of all available controllers
373 static int pakfire_cgroup_available_controllers(struct pakfire_cgroup
* cgroup
) {
374 return pakfire_cgroup_read_controllers(cgroup
, "cgroup.controllers");
378 Returns a bitmap of all enabled controllers
380 static int pakfire_cgroup_enabled_controllers(struct pakfire_cgroup
* cgroup
) {
381 return pakfire_cgroup_read_controllers(cgroup
, "cgroup.subtree_control");
385 This function takes a bitmap of controllers that should be enabled.
387 static int pakfire_cgroup_enable_controllers(struct pakfire_cgroup
* cgroup
,
388 enum pakfire_cgroup_controllers controllers
) {
389 struct pakfire_cgroup
* parent
= NULL
;
392 // Find all enabled controllers
393 const int enabled_controllers
= pakfire_cgroup_enabled_controllers(cgroup
);
394 if (enabled_controllers
< 0) {
395 ERROR(cgroup
->pakfire
, "Could not fetch enabled controllers: %m\n");
399 // Filter out anything that is already enabled
400 controllers
= (controllers
& ~enabled_controllers
);
402 // Exit if everything is already enabled
404 DEBUG(cgroup
->pakfire
, "All controllers are already enabled\n");
408 // Find all available controllers
409 const int available_controllers
= pakfire_cgroup_available_controllers(cgroup
);
410 if (available_controllers
< 0) {
411 ERROR(cgroup
->pakfire
, "Could not fetch available controllers: %m\n");
415 // Are all controllers we need available, yet?
416 if (controllers
& ~available_controllers
) {
417 DEBUG(cgroup
->pakfire
, "Not all controllers are available, yet\n");
419 parent
= pakfire_cgroup_parent(cgroup
);
421 // Enable everything we need on the parent group
423 r
= pakfire_cgroup_enable_controllers(parent
, controllers
);
429 // Determine how many iterations we will need
430 const int iterations
= 1 << (sizeof(controllers
) * 8 - __builtin_clz(controllers
));
432 // Iterate over all known controllers
433 for (int controller
= 1; controller
< iterations
; controller
<<= 1) {
434 // Skip enabling this controller if not requested
435 if (!(controller
& controllers
))
439 const char* name
= pakfire_cgroup_controller_name(controller
);
441 DEBUG(cgroup
->pakfire
, "Enabling controller %s in cgroup %s\n",
442 name
, pakfire_cgroup_name(cgroup
));
444 // Try enabling the controller (this will succeed if it already is enabled)
445 r
= pakfire_cgroup_write(cgroup
, "cgroup.subtree_control", "+%s\n", name
);
447 ERROR(cgroup
->pakfire
, "Could not enable controller %s in cgroup %s\n",
448 name
, pakfire_cgroup_name(cgroup
));
455 pakfire_cgroup_unref(parent
);
460 static int pakfire_cgroup_enable_accounting(struct pakfire_cgroup
* cgroup
) {
461 // Enable all accounting controllers
462 return pakfire_cgroup_enable_controllers(cgroup
,
463 pakfire_cgroup_accounting_controllers
);
467 Entry function to open a new cgroup.
469 If the cgroup doesn't exist, it will be created including any parent cgroups.
471 int pakfire_cgroup_open(struct pakfire_cgroup
** cgroup
,
472 struct pakfire
* pakfire
, const char* path
, int flags
) {
475 // Allocate the cgroup struct
476 struct pakfire_cgroup
* c
= calloc(1, sizeof(*c
));
480 DEBUG(pakfire
, "Allocated cgroup %s at %p\n", path
, c
);
482 // Keep a reference to pakfire
483 c
->pakfire
= pakfire_ref(pakfire
);
485 // Initialize reference counter
489 pakfire_string_set(c
->path
, path
);
494 // Open a file descriptor
495 c
->fd
= __pakfire_cgroup_open(c
);
499 // Enable accounting if requested
500 if (pakfire_cgroup_has_flag(c
, PAKFIRE_CGROUP_ENABLE_ACCOUNTING
)) {
501 r
= pakfire_cgroup_enable_accounting(c
);
510 pakfire_cgroup_free(c
);
514 struct pakfire_cgroup
* pakfire_cgroup_ref(struct pakfire_cgroup
* cgroup
) {
520 struct pakfire_cgroup
* pakfire_cgroup_unref(struct pakfire_cgroup
* cgroup
) {
521 if (--cgroup
->nrefs
> 0)
524 pakfire_cgroup_free(cgroup
);
528 // Open a child cgroup
529 int pakfire_cgroup_child(struct pakfire_cgroup
** child
,
530 struct pakfire_cgroup
* cgroup
, const char* name
, int flags
) {
541 r
= pakfire_path_join(path
, cgroup
->path
, name
);
545 // Open the child group
546 return pakfire_cgroup_open(child
, cgroup
->pakfire
, path
, flags
);
549 static int pakfire_cgroup_procs_callback(struct pakfire_cgroup
* cgroup
,
550 int (*callback
)(struct pakfire_cgroup
* cgroup
, pid_t pid
, void* data
), void* data
) {
553 // Check if we have a callback
560 FILE* f
= pakfire_cgroup_open_file(cgroup
, "cgroup.procs", "r");
567 // Walk through all PIDs
569 ssize_t bytes_read
= getline(&line
, &l
, f
);
574 pid_t pid
= strtol(line
, NULL
, 10);
576 // Call callback function
577 r
= callback(cgroup
, pid
, data
);
591 static int send_sigkill(struct pakfire_cgroup
* cgroup
, const pid_t pid
, void* data
) {
592 DEBUG(cgroup
->pakfire
, "Sending signal SIGKILL to PID %d\n", pid
);
594 int r
= kill(pid
, SIGKILL
);
595 if (r
< 0 && errno
!= ESRCH
) {
596 ERROR(cgroup
->pakfire
, "Could not send signal SIGKILL to PID %d: %m\n", pid
);
604 Immediately kills all processes in this cgroup
606 static int pakfire_cgroup_killall(struct pakfire_cgroup
* cgroup
) {
607 DEBUG(cgroup
->pakfire
, "%s: Killing all processes\n", pakfire_cgroup_name(cgroup
));
609 // Do we have support for cgroup.kill?
610 int r
= pakfire_cgroup_access(cgroup
, "cgroup.kill", F_OK
, 0);
612 // Fall back to the legacy version
613 if (r
&& errno
== ENOENT
) {
614 return pakfire_cgroup_procs_callback(cgroup
, send_sigkill
, NULL
);
617 return pakfire_cgroup_write(cgroup
, "cgroup.kill", "1");
621 Immediately destroys this cgroup
623 int pakfire_cgroup_destroy(struct pakfire_cgroup
* cgroup
) {
626 // Cannot call this for the root group
627 if (pakfire_cgroup_is_root(cgroup
)) {
632 DEBUG(cgroup
->pakfire
, "Destroying cgroup %s\n", pakfire_cgroup_name(cgroup
));
634 // Kill everything in this group
635 r
= pakfire_cgroup_killall(cgroup
);
639 // Close the file descriptor
645 // Open the root directory
646 int fd
= pakfire_cgroup_open_root(cgroup
);
650 // Delete the directory
651 r
= unlinkat(fd
, cgroup
->path
, AT_REMOVEDIR
);
653 ERROR(cgroup
->pakfire
, "Could not destroy cgroup: %m\n");
661 int pakfire_cgroup_fd(struct pakfire_cgroup
* cgroup
) {
667 int pakfire_cgroup_set_guaranteed_memory(struct pakfire_cgroup
* cgroup
, size_t mem
) {
670 // Enable memory controller
671 r
= pakfire_cgroup_enable_controllers(cgroup
, PAKFIRE_CGROUP_CONTROLLER_MEMORY
);
675 DEBUG(cgroup
->pakfire
, "%s: Setting guaranteed memory to %zu byte(s)\n",
676 pakfire_cgroup_name(cgroup
), mem
);
679 r
= pakfire_cgroup_write(cgroup
, "memory.min", "%zu\n", mem
);
681 ERROR(cgroup
->pakfire
, "%s: Could not set guaranteed memory: %m\n",
682 pakfire_cgroup_name(cgroup
));
687 int pakfire_cgroup_set_memory_limit(struct pakfire_cgroup
* cgroup
, size_t mem
) {
690 // Enable memory controller
691 r
= pakfire_cgroup_enable_controllers(cgroup
, PAKFIRE_CGROUP_CONTROLLER_MEMORY
);
695 DEBUG(cgroup
->pakfire
, "%s: Setting memory limit to %zu byte(s)\n",
696 pakfire_cgroup_name(cgroup
), mem
);
699 r
= pakfire_cgroup_write(cgroup
, "memory.max", "%zu\n", mem
);
701 ERROR(cgroup
->pakfire
, "%s: Could not set memory limit: %m\n",
702 pakfire_cgroup_name(cgroup
));
709 int pakfire_cgroup_set_pid_limit(struct pakfire_cgroup
* cgroup
, size_t limit
) {
712 // Enable PID controller
713 r
= pakfire_cgroup_enable_controllers(cgroup
, PAKFIRE_CGROUP_CONTROLLER_PIDS
);
717 DEBUG(cgroup
->pakfire
, "%s: Setting PID limit to %zu\n",
718 pakfire_cgroup_name(cgroup
), limit
);
721 r
= pakfire_cgroup_write(cgroup
, "pids.max", "%zu\n", limit
);
723 ERROR(cgroup
->pakfire
, "%s: Could not set PID limit: %m\n",
724 pakfire_cgroup_name(cgroup
));
731 static int __pakfire_cgroup_read_stats_line(struct pakfire_cgroup
* cgroup
,
732 int (*callback
)(struct pakfire_cgroup
* cgroup
, const char* key
, unsigned long val
, void* data
),
733 void* data
, char* line
) {
736 DEBUG(cgroup
->pakfire
, "Parsing line: %s\n", line
);
739 unsigned long val
= 0;
741 // Number of the field
744 char* elem
= strtok_r(line
, " ", &p
);
747 // First field is the key
750 pakfire_string_set(key
, elem
);
753 // The second field is some value
755 val
= strtoul(elem
, NULL
, 10);
760 DEBUG(cgroup
->pakfire
, "%s: Unknown value in cgroup stats (%d): %s\n",
761 pakfire_cgroup_name(cgroup
), i
, elem
);
765 elem
= strtok_r(NULL
, " ", &p
);
768 // Check if we parsed both fields
770 ERROR(cgroup
->pakfire
, "Could not parse line\n");
775 return callback(cgroup
, key
, val
, data
);
778 static int __pakfire_cgroup_read_stats(struct pakfire_cgroup
* cgroup
, const char* path
,
779 int (*callback
)(struct pakfire_cgroup
* cgroup
, const char* key
, unsigned long val
, void* data
),
784 char buffer
[BUFFER_SIZE
];
786 DEBUG(cgroup
->pakfire
, "%s: Reading stats from %s\n", pakfire_cgroup_name(cgroup
), path
);
789 r
= pakfire_cgroup_read(cgroup
, path
, buffer
, sizeof(buffer
));
793 char* line
= strtok_r(buffer
, "\n", &p
);
796 r
= __pakfire_cgroup_read_stats_line(cgroup
, callback
, data
, line
);
800 // Move to the next line
801 line
= strtok_r(NULL
, "\n", &p
);
808 struct pakfire_cgroup_stat_entry
{
813 static int __pakfire_cgroup_parse_cpu_stats(struct pakfire_cgroup
* cgroup
,
814 const char* key
, unsigned long val
, void* data
) {
815 struct pakfire_cgroup_cpu_stats
* stats
= (struct pakfire_cgroup_cpu_stats
*)data
;
817 const struct pakfire_cgroup_stat_entry entries
[] = {
818 { "system_usec", &stats
->system_usec
},
819 { "usage_usec", &stats
->usage_usec
},
820 { "user_usec", &stats
->user_usec
},
823 // Find and store value
824 for (const struct pakfire_cgroup_stat_entry
* entry
= entries
; entry
->key
; entry
++) {
825 if (strcmp(entry
->key
, key
) == 0) {
831 DEBUG(cgroup
->pakfire
, "Unknown key for CPU stats: %s = %ld\n", key
, val
);
836 static int __pakfire_cgroup_parse_memory_stats(struct pakfire_cgroup
* cgroup
,
837 const char* key
, unsigned long val
, void* data
) {
838 struct pakfire_cgroup_memory_stats
* stats
= (struct pakfire_cgroup_memory_stats
*)data
;
840 const struct pakfire_cgroup_stat_entry entries
[] = {
841 { "anon", &stats
->anon
},
842 { "file", &stats
->file
},
843 { "kernel", &stats
->kernel
},
844 { "kernel_stack", &stats
->kernel_stack
},
845 { "pagetables", &stats
->pagetables
},
846 { "percpu", &stats
->percpu
},
847 { "sock", &stats
->sock
},
848 { "vmalloc", &stats
->vmalloc
},
849 { "shmem", &stats
->shmem
},
850 { "zswap", &stats
->zswap
},
851 { "zswapped", &stats
->zswapped
},
852 { "file_mapped", &stats
->file_mapped
},
853 { "file_dirty", &stats
->file_dirty
},
854 { "file_writeback", &stats
->file_writeback
},
855 { "swapcached", &stats
->swapcached
},
856 { "anon_thp", &stats
->anon_thp
},
857 { "file_thp", &stats
->file_thp
},
858 { "shmem_thp", &stats
->shmem_thp
},
859 { "inactive_anon", &stats
->inactive_anon
},
860 { "active_anon", &stats
->active_anon
},
861 { "inactive_file", &stats
->inactive_file
},
862 { "active_file", &stats
->active_file
},
863 { "unevictable", &stats
->unevictable
},
864 { "slab_reclaimable", &stats
->slab_reclaimable
},
865 { "slab_unreclaimable", &stats
->slab_unreclaimable
},
866 { "slab", &stats
->slab
},
867 { "workingset_refault_anon", &stats
->workingset_refault_anon
},
868 { "workingset_refault_file", &stats
->workingset_refault_file
},
869 { "workingset_activate_anon", &stats
->workingset_activate_anon
},
870 { "workingset_activate_file", &stats
->workingset_activate_file
},
871 { "workingset_restore_anon", &stats
->workingset_restore_anon
},
872 { "workingset_restore_file", &stats
->workingset_restore_file
},
873 { "workingset_nodereclaim", &stats
->workingset_nodereclaim
},
874 { "pgfault", &stats
->pgfault
},
875 { "pgmajfault", &stats
->pgmajfault
},
876 { "pgrefill", &stats
->pgrefill
},
877 { "pgscan", &stats
->pgscan
},
878 { "pgsteal", &stats
->pgsteal
},
879 { "pgactivate", &stats
->pgactivate
},
880 { "pgdeactivate", &stats
->pgdeactivate
},
881 { "pglazyfree", &stats
->pglazyfree
},
882 { "pglazyfreed", &stats
->pglazyfreed
},
883 { "thp_fault_alloc", &stats
->thp_fault_alloc
},
884 { "thp_collapse_alloc", &stats
->thp_collapse_alloc
},
888 // Find and store value
889 for (const struct pakfire_cgroup_stat_entry
* entry
= entries
; entry
->key
; entry
++) {
890 if (strcmp(entry
->key
, key
) == 0) {
896 // Log any unknown keys
897 DEBUG(cgroup
->pakfire
, "Unknown key for memory stats: %s = %ld\n", key
, val
);
902 int pakfire_cgroup_stat(struct pakfire_cgroup
* cgroup
,
903 struct pakfire_cgroup_stats
* stats
) {
913 r
= __pakfire_cgroup_read_stats(cgroup
, "cpu.stat",
914 __pakfire_cgroup_parse_cpu_stats
, &stats
->cpu
);
919 r
= __pakfire_cgroup_read_stats(cgroup
, "memory.stat",
920 __pakfire_cgroup_parse_memory_stats
, &stats
->memory
);
926 ERROR(cgroup
->pakfire
, "%s: Could not read cgroup stats: %m\n",
927 pakfire_cgroup_name(cgroup
));
932 int pakfire_cgroup_stat_dump(struct pakfire_cgroup
* cgroup
,
933 const struct pakfire_cgroup_stats
* stats
) {
940 DEBUG(cgroup
->pakfire
, "%s: Total CPU time usage: %lu\n",
941 pakfire_cgroup_name(cgroup
), stats
->cpu
.usage_usec
);