]> arthur.barton.de Git - netdata.git/blob - src/sys_fs_cgroup.c
added dirty and swap to cgroup
[netdata.git] / src / sys_fs_cgroup.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <inttypes.h>
8 #include <sys/types.h>
9 #include <dirent.h>
10 #include <string.h>
11
12 #include "common.h"
13 #include "appconfig.h"
14 #include "procfile.h"
15 #include "log.h"
16 #include "rrd.h"
17
18 // ----------------------------------------------------------------------------
19 // cgroup globals
20
21 static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
22 static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
23 static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
24 static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
25 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
26 static int cgroup_check_for_new_every = 10;
27 static char *cgroup_cpuacct_base = NULL;
28 static char *cgroup_blkio_base = NULL;
29 static char *cgroup_memory_base = NULL;
30
31 static int cgroup_root_count = 0;
32 static int cgroup_root_max = 50;
33 static int cgroup_max_depth = 0;
34
35 void read_cgroup_plugin_configuration() {
36         cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new plugin every", cgroup_check_for_new_every);
37
38         cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
39         cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
40         cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
41         cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
42
43         char filename[FILENAME_MAX + 1];
44         snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/cpuacct");
45         cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
46
47         snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/blkio");
48         cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
49
50         snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/memory");
51         cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
52
53         cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
54         cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
55
56         cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
57 }
58
59 // ----------------------------------------------------------------------------
60 // cgroup objects
61
62 struct blkio {
63         int updated;
64
65         char *filename;
66
67         unsigned long long Read;
68         unsigned long long Write;
69         unsigned long long Sync;
70         unsigned long long Async;
71         unsigned long long Total;
72 };
73
74 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
75 struct memory {
76         int updated;
77
78         char *filename;
79
80         int has_dirty_swap;
81
82         unsigned long long cache;
83         unsigned long long rss;
84         unsigned long long rss_huge;
85         unsigned long long mapped_file;
86         unsigned long long writeback;
87         unsigned long long dirty;
88         unsigned long long swap;
89         unsigned long long pgpgin;
90         unsigned long long pgpgout;
91         unsigned long long pgfault;
92         unsigned long long pgmajfault;
93 /*
94         unsigned long long inactive_anon;
95         unsigned long long active_anon;
96         unsigned long long inactive_file;
97         unsigned long long active_file;
98         unsigned long long unevictable;
99         unsigned long long hierarchical_memory_limit;
100         unsigned long long total_cache;
101         unsigned long long total_rss;
102         unsigned long long total_rss_huge;
103         unsigned long long total_mapped_file;
104         unsigned long long total_writeback;
105         unsigned long long total_dirty;
106         unsigned long long total_swap;
107         unsigned long long total_pgpgin;
108         unsigned long long total_pgpgout;
109         unsigned long long total_pgfault;
110         unsigned long long total_pgmajfault;
111         unsigned long long total_inactive_anon;
112         unsigned long long total_active_anon;
113         unsigned long long total_inactive_file;
114         unsigned long long total_active_file;
115         unsigned long long total_unevictable;
116 */
117 };
118
119 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
120 struct cpuacct_stat {
121         int updated;
122
123         char *filename;
124
125         unsigned long long user;
126         unsigned long long system;
127 };
128
129 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
130 struct cpuacct_usage {
131         int updated;
132
133         char *filename;
134
135         unsigned int cpus;
136         unsigned long long *cpu_percpu;
137 };
138
139 struct cgroup {
140         int available;
141         int enabled;
142
143         char *id;
144         uint32_t hash;
145
146         char *name;
147
148         struct cpuacct_stat cpuacct_stat;
149         struct cpuacct_usage cpuacct_usage;
150
151         struct memory memory;
152
153         struct blkio io_service_bytes;                          // bytes
154         struct blkio io_serviced;                                       // operations
155
156         struct blkio throttle_io_service_bytes;         // bytes
157         struct blkio throttle_io_serviced;                      // operations
158
159         struct blkio io_merged;                                         // operations
160         struct blkio io_queued;                                         // operations
161
162         struct cgroup *next;
163
164 } *cgroup_root = NULL;
165
166 // ----------------------------------------------------------------------------
167 // read values from /sys
168
169 void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
170         static procfile *ff = NULL;
171
172         static uint32_t user_hash = 0;
173         static uint32_t system_hash = 0;
174
175         if(unlikely(user_hash == 0)) {
176                 user_hash = simple_hash("user");
177                 system_hash = simple_hash("system");
178         }
179
180         cp->updated = 0;
181         if(cp->filename) {
182                 ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
183                 if(!ff) return;
184
185                 ff = procfile_readall(ff);
186                 if(!ff) return;
187
188                 unsigned long i, lines = procfile_lines(ff);
189
190                 if(lines < 1) {
191                         error("File '%s' should have 1+ lines.", cp->filename);
192                         return;
193                 }
194
195                 for(i = 0; i < lines ; i++) {
196                         char *s = procfile_lineword(ff, i, 0);
197                         uint32_t hash = simple_hash(s);
198
199                         if(hash == user_hash && !strcmp(s, "user"))
200                                 cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
201
202                         else if(hash == system_hash && !strcmp(s, "system"))
203                                 cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
204                 }
205
206                 cp->updated = 1;
207
208                 // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
209         }
210 }
211
212 void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
213         static procfile *ff = NULL;
214
215         ca->updated = 0;
216         if(ca->filename) {
217                 ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
218                 if(!ff) return;
219
220                 ff = procfile_readall(ff);
221                 if(!ff) return;
222
223                 if(procfile_lines(ff) < 1) {
224                         error("File '%s' should have 1+ lines but has %d.", ca->filename, procfile_lines(ff));
225                         return;
226                 }
227
228                 unsigned long i = procfile_linewords(ff, 0);
229                 if(i <= 0) return;
230
231                 // we may have 1 more CPU reported
232                 while(i > 0) {
233                         char *s = procfile_lineword(ff, 0, i - 1);
234                         if(!*s) i--;
235                         else break;
236                 }
237
238                 if(i != ca->cpus) {
239                         free(ca->cpu_percpu);
240                 }
241                 ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
242                 if(!ca->cpu_percpu)
243                         fatal("Cannot allocate memory (%z bytes)", sizeof(unsigned long long) * i);
244
245                 ca->cpus = i;
246
247                 for(i = 0; i < ca->cpus ;i++) {
248                         ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
249                         // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
250                 }
251
252                 ca->updated = 1;
253         }
254 }
255
256 void cgroup_read_blkio(struct blkio *io) {
257         static procfile *ff = NULL;
258
259         static uint32_t Read_hash = 0;
260         static uint32_t Write_hash = 0;
261         static uint32_t Sync_hash = 0;
262         static uint32_t Async_hash = 0;
263         static uint32_t Total_hash = 0;
264
265         if(unlikely(Read_hash == 0)) {
266                 Read_hash = simple_hash("Read");
267                 Write_hash = simple_hash("Write");
268                 Sync_hash = simple_hash("Sync");
269                 Async_hash = simple_hash("Async");
270                 Total_hash = simple_hash("Total");
271         }
272
273         io->updated = 0;
274         if(io->filename) {
275                 ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
276                 if(!ff) return;
277
278                 ff = procfile_readall(ff);
279                 if(!ff) return;
280
281                 unsigned long i, lines = procfile_lines(ff);
282
283                 if(lines < 1) {
284                         error("File '%s' should have 1+ lines.", io->filename);
285                         return;
286                 }
287
288                 io->Read = 0;
289                 io->Write = 0;
290                 io->Sync = 0;
291                 io->Async = 0;
292                 io->Total = 0;
293
294                 for(i = 0; i < lines ; i++) {
295                         char *s = procfile_lineword(ff, i, 1);
296                         uint32_t hash = simple_hash(s);
297
298                         if(hash == Read_hash && !strcmp(s, "Read"))
299                                 io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
300
301                         else if(hash == Write_hash && !strcmp(s, "Write"))
302                                 io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
303
304                         else if(hash == Sync_hash && !strcmp(s, "Sync"))
305                                 io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
306
307                         else if(hash == Async_hash && !strcmp(s, "Async"))
308                                 io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
309
310                         else if(hash == Total_hash && !strcmp(s, "Total"))
311                                 io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
312                 }
313
314                 io->updated = 1;
315                 // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
316         }
317 }
318
319 void cgroup_read_memory(struct memory *mem) {
320         static procfile *ff = NULL;
321
322         static uint32_t cache_hash = 0;
323         static uint32_t rss_hash = 0;
324         static uint32_t rss_huge_hash = 0;
325         static uint32_t mapped_file_hash = 0;
326         static uint32_t writeback_hash = 0;
327         static uint32_t dirty_hash = 0;
328         static uint32_t swap_hash = 0;
329         static uint32_t pgpgin_hash = 0;
330         static uint32_t pgpgout_hash = 0;
331         static uint32_t pgfault_hash = 0;
332         static uint32_t pgmajfault_hash = 0;
333 /*
334         static uint32_t inactive_anon_hash = 0;
335         static uint32_t active_anon_hash = 0;
336         static uint32_t inactive_file_hash = 0;
337         static uint32_t active_file_hash = 0;
338         static uint32_t unevictable_hash = 0;
339         static uint32_t hierarchical_memory_limit_hash = 0;
340         static uint32_t total_cache_hash = 0;
341         static uint32_t total_rss_hash = 0;
342         static uint32_t total_rss_huge_hash = 0;
343         static uint32_t total_mapped_file_hash = 0;
344         static uint32_t total_writeback_hash = 0;
345         static uint32_t total_dirty_hash = 0;
346         static uint32_t total_swap_hash = 0;
347         static uint32_t total_pgpgin_hash = 0;
348         static uint32_t total_pgpgout_hash = 0;
349         static uint32_t total_pgfault_hash = 0;
350         static uint32_t total_pgmajfault_hash = 0;
351         static uint32_t total_inactive_anon_hash = 0;
352         static uint32_t total_active_anon_hash = 0;
353         static uint32_t total_inactive_file_hash = 0;
354         static uint32_t total_active_file_hash = 0;
355         static uint32_t total_unevictable_hash = 0;
356 */
357         if(unlikely(cache_hash == 0)) {
358                 cache_hash = simple_hash("cache");
359                 rss_hash = simple_hash("rss");
360                 rss_huge_hash = simple_hash("rss_huge");
361                 mapped_file_hash = simple_hash("mapped_file");
362                 writeback_hash = simple_hash("writeback");
363                 dirty_hash = simple_hash("dirty");
364                 swap_hash = simple_hash("swap");
365                 pgpgin_hash = simple_hash("pgpgin");
366                 pgpgout_hash = simple_hash("pgpgout");
367                 pgfault_hash = simple_hash("pgfault");
368                 pgmajfault_hash = simple_hash("pgmajfault");
369 /*
370                 inactive_anon_hash = simple_hash("inactive_anon");
371                 active_anon_hash = simple_hash("active_anon");
372                 inactive_file_hash = simple_hash("inactive_file");
373                 active_file_hash = simple_hash("active_file");
374                 unevictable_hash = simple_hash("unevictable");
375                 hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
376                 total_cache_hash = simple_hash("total_cache");
377                 total_rss_hash = simple_hash("total_rss");
378                 total_rss_huge_hash = simple_hash("total_rss_huge");
379                 total_mapped_file_hash = simple_hash("total_mapped_file");
380                 total_writeback_hash = simple_hash("total_writeback");
381                 total_dirty_hash = simple_hash("total_dirty");
382                 total_swap_hash = simple_hash("total_swap");
383                 total_pgpgin_hash = simple_hash("total_pgpgin");
384                 total_pgpgout_hash = simple_hash("total_pgpgout");
385                 total_pgfault_hash = simple_hash("total_pgfault");
386                 total_pgmajfault_hash = simple_hash("total_pgmajfault");
387                 total_inactive_anon_hash = simple_hash("total_inactive_anon");
388                 total_active_anon_hash = simple_hash("total_active_anon");
389                 total_inactive_file_hash = simple_hash("total_inactive_file");
390                 total_active_file_hash = simple_hash("total_active_file");
391                 total_unevictable_hash = simple_hash("total_unevictable");
392 */
393         }
394
395         mem->updated = 0;
396         if(mem->filename) {
397                 ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
398                 if(!ff) return;
399
400                 ff = procfile_readall(ff);
401                 if(!ff) return;
402
403                 unsigned long i, lines = procfile_lines(ff);
404
405                 if(lines < 1) {
406                         error("File '%s' should have 1+ lines.", mem->filename);
407                         return;
408                 }
409
410                 for(i = 0; i < lines ; i++) {
411                         char *s = procfile_lineword(ff, i, 0);
412                         uint32_t hash = simple_hash(s);
413
414                         if(hash == cache_hash && !strcmp(s, "cache"))
415                                 mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
416
417                         else if(hash == rss_hash && !strcmp(s, "rss"))
418                                 mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
419
420                         else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
421                                 mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
422
423                         else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
424                                 mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
425
426                         else if(hash == writeback_hash && !strcmp(s, "writeback"))
427                                 mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
428
429                         else if(hash == dirty_hash && !strcmp(s, "dirty")) {
430                                 mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
431                                 mem->has_dirty_swap = 1;
432                         }
433
434                         else if(hash == swap_hash && !strcmp(s, "swap")) {
435                                 mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
436                                 mem->has_dirty_swap = 1;
437                         }
438
439                         else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
440                                 mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
441
442                         else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
443                                 mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
444
445                         else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
446                                 mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
447
448                         else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
449                                 mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
450
451 /*
452                         else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
453                                 mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
454
455                         else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
456                                 mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
457
458                         else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
459                                 mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
460
461                         else if(hash == active_file_hash && !strcmp(s, "active_file"))
462                                 mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
463
464                         else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
465                                 mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
466
467                         else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
468                                 mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
469
470                 else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
471                                 mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
472
473                         else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
474                                 mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
475
476                         else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
477                                 mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
478
479                         else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
480                                 mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
481
482                         else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
483                                 mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
484
485                         else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
486                                 mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
487
488                         else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
489                                 mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
490
491                         else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
492                                 mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
493
494                         else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
495                                 mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
496
497                         else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
498                                 mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
499
500                         else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
501                                 mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
502
503                         else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
504                                 mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
505
506                         else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
507                                 mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
508
509                         else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
510                                 mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
511
512                         else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
513                                 mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
514
515                         else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
516                                 mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
517 */
518                 }
519
520                 // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
521
522                 mem->updated = 1;
523         }
524 }
525
526 void cgroup_read(struct cgroup *cg) {
527         cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
528         cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
529         cgroup_read_memory(&cg->memory);
530         cgroup_read_blkio(&cg->io_service_bytes);
531         cgroup_read_blkio(&cg->io_serviced);
532         cgroup_read_blkio(&cg->throttle_io_service_bytes);
533         cgroup_read_blkio(&cg->throttle_io_serviced);
534         cgroup_read_blkio(&cg->io_merged);
535         cgroup_read_blkio(&cg->io_queued);
536 }
537
538 void read_all_cgroups(struct cgroup *cg) {
539         struct cgroup *i;
540
541         for(i = cg; i ; i = i->next)
542                 cgroup_read(i);
543 }
544
545 // ----------------------------------------------------------------------------
546 // add/remove/find cgroup objects
547
548 struct cgroup *cgroup_add(const char *id) {
549         if(cgroup_root_count >= cgroup_root_max) {
550                 info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
551                 return NULL;
552         }
553
554         int def = cgroup_enable_new_cgroups_detected_at_runtime;
555         const char *name = id;
556         if(!*name) {
557                 name = "/";
558
559                 // disable by default the host cgroup
560                 def = 0;
561         }
562         else {
563                 if(*name == '/') name++;
564
565                 // disable by default the parent cgroup
566                 // for known cgroup managers
567                 if(!strcmp(name, "lxc") || !strcmp(name, "docker"))
568                         def = 0;
569         }
570
571         char option[FILENAME_MAX + 1];
572         snprintf(option, FILENAME_MAX, "enable cgroup %s", name);
573         if(!config_get_boolean("plugin:cgroups", option, def))
574                 return NULL;
575
576         struct cgroup *cg = calloc(1, sizeof(struct cgroup));
577         if(cg) {
578                 cg->id = strdup(id);
579                 if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
580
581                 cg->hash = simple_hash(cg->id);
582
583                 cg->name = strdup(name);
584                 if(!cg->name) fatal("Cannot allocate memory for cgroup '%s'", id);
585
586                 if(!cgroup_root)
587                         cgroup_root = cg;
588                 else {
589                         // append it
590                         struct cgroup *e;
591                         for(e = cgroup_root; e->next ;e = e->next) ;
592                         e->next = cg;
593                 }
594
595                 cgroup_root_count++;
596
597                 // fprintf(stderr, " > added cgroup No %d, with id '%s' (%u) and name '%s'\n", cgroup_root_count, cg->id, cg->hash, cg->name);
598         }
599         else fatal("Cannot allocate memory for cgroup '%s'", id);
600
601         return cg;
602 }
603
604 void cgroup_remove(struct cgroup *cg) {
605         if(cg == cgroup_root) {
606                 cgroup_root = cg->next;
607         }
608         else {
609                 struct cgroup *e;
610                 for(e = cgroup_root; e->next ;e = e->next)
611                         if(unlikely(e->next == cg)) break;
612
613                 if(e->next != cg) {
614                         error("Cannot find cgroup '%s' in list of cgroups", cg->id);
615                 }
616                 else {
617                         e->next = cg->next;
618                         cg->next = NULL;
619                 }
620         }
621
622         free(cg->cpuacct_usage.cpu_percpu);
623
624         free(cg->cpuacct_stat.filename);
625         free(cg->cpuacct_usage.filename);
626         free(cg->memory.filename);
627         free(cg->io_service_bytes.filename);
628         free(cg->io_serviced.filename);
629         free(cg->throttle_io_service_bytes.filename);
630         free(cg->throttle_io_serviced.filename);
631         free(cg->io_merged.filename);
632         free(cg->io_queued.filename);
633
634         free(cg->id);
635         free(cg->name);
636         free(cg);
637 }
638
639 // find if a given cgroup exists
640 struct cgroup *cgroup_find(const char *id) {
641         uint32_t hash = simple_hash(id);
642
643         // fprintf(stderr, " > searching for '%s' (%u)\n", id, hash);
644
645         struct cgroup *cg;
646         for(cg = cgroup_root; cg ; cg = cg->next) {
647                 if(hash == cg->hash && strcmp(id, cg->id) == 0)
648                         break;
649         }
650
651         return cg;
652 }
653
654 // ----------------------------------------------------------------------------
655 // detect running cgroups
656
657 // callback for find_file_in_subdirs()
658 void found_dir_in_subdir(const char *dir) {
659         // fprintf(stderr, "found dir '%s'\n", dir);
660
661         struct cgroup *cg = cgroup_find(dir);
662         if(!cg) {
663                 if(*dir && cgroup_max_depth > 0) {
664                         int depth = 0;
665                         const char *s;
666
667                         for(s = dir; *s ;s++)
668                                 if(unlikely(*s == '/'))
669                                         depth++;
670
671                         if(depth > cgroup_max_depth) {
672                                 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
673                                 return;
674                         }
675                 }
676                 cg = cgroup_add(dir);
677         }
678
679         if(cg) cg->available = 1;
680 }
681
682 void find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
683         if(!this) this = base;
684         size_t dirlen = strlen(this), baselen = strlen(base);
685
686         DIR *dir = opendir(this);
687         if(!dir) return;
688
689         callback(&this[baselen]);
690
691         struct dirent *de = NULL;
692         while((de = readdir(dir))) {
693                 if(de->d_type == DT_DIR
694                         && (
695                                 (de->d_name[0] == '.' && de->d_name[1] == '\0')
696                                 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
697                                 ))
698                         continue;
699
700                 // fprintf(stderr, "examining '%s/%s'\n", this, de->d_name);
701
702                 if(de->d_type == DT_DIR) {
703                         char *s = malloc(dirlen + strlen(de->d_name) + 2);
704                         if(s) {
705                                 strcpy(s, this);
706                                 strcat(s, "/");
707                                 strcat(s, de->d_name);
708                                 find_dir_in_subdirs(base, s, callback);
709                                 free(s);
710                         }
711                 }
712         }
713
714         closedir(dir);
715 }
716
717 void mark_all_cgroups_as_not_available() {
718         struct cgroup *cg;
719
720         // mark all as not available
721         for(cg = cgroup_root; cg ; cg = cg->next)
722                 cg->available = 0;
723 }
724
725 struct cgroup *find_all_cgroups() {
726
727         mark_all_cgroups_as_not_available();
728
729         if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
730                 find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_dir_in_subdir);
731
732         if(cgroup_enable_blkio)
733                 find_dir_in_subdirs(cgroup_blkio_base, NULL, found_dir_in_subdir);
734
735         if(cgroup_enable_memory)
736                 find_dir_in_subdirs(cgroup_memory_base, NULL, found_dir_in_subdir);
737
738         struct cgroup *cg;
739         for(cg = cgroup_root; cg ; cg = cg->next) {
740                 // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
741
742                 if(!cg->available) {
743                         cgroup_remove(cg);
744                         continue;
745                 }
746
747                 // check for newly added cgroups
748                 // and update the filenames they read
749                 char filename[FILENAME_MAX + 1];
750                 if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
751                         snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
752                         cg->cpuacct_stat.filename = strdup(filename);
753                 }
754                 if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
755                         snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
756                         cg->cpuacct_usage.filename = strdup(filename);
757                 }
758                 if(cgroup_enable_memory && !cg->memory.filename) {
759                         snprintf(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
760                         cg->memory.filename = strdup(filename);
761                 }
762                 if(cgroup_enable_blkio) {
763                         if(!cg->io_service_bytes.filename) {
764                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
765                                 cg->io_service_bytes.filename = strdup(filename);
766                         }
767                         if(!cg->io_serviced.filename) {
768                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
769                                 cg->io_serviced.filename = strdup(filename);
770                         }
771                         if(!cg->throttle_io_service_bytes.filename) {
772                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
773                                 cg->throttle_io_service_bytes.filename = strdup(filename);
774                         }
775                         if(!cg->throttle_io_serviced.filename) {
776                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
777                                 cg->throttle_io_serviced.filename = strdup(filename);
778                         }
779                         if(!cg->io_merged.filename) {
780                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
781                                 cg->io_merged.filename = strdup(filename);
782                         }
783                         if(!cg->io_queued.filename) {
784                                 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
785                                 cg->io_queued.filename = strdup(filename);
786                         }
787                 }
788         }
789
790         return cgroup_root;
791 }
792
793 // ----------------------------------------------------------------------------
794 // generate charts
795
796 #define CHART_TITLE_MAX 300
797
798 void update_cgroup_charts(int update_every) {
799         char type[RRD_ID_LENGTH_MAX + 1];
800         char title[CHART_TITLE_MAX + 1];
801
802         struct cgroup *cg;
803         RRDSET *st;
804
805         for(cg = cgroup_root; cg ; cg = cg->next) {
806                 if(!cg->available) continue;
807
808                 if(cg->id[0] == '\0')
809                         strcpy(type, "cgroup_host");
810                 else if(cg->id[0] == '/')
811                         snprintf(type, RRD_ID_LENGTH_MAX, "cgroup%s", cg->id);
812                 else
813                         snprintf(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->id);
814
815                 netdata_fix_chart_id(type);
816
817                 if(cg->cpuacct_stat.updated) {
818                         st = rrdset_find_bytype(type, "cpu");
819                         if(!st) {
820                                 snprintf(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->name);
821                                 st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
822
823                                 rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
824                                 rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
825                         }
826                         else rrdset_next(st);
827
828                         rrddim_set(st, "user", cg->cpuacct_stat.user);
829                         rrddim_set(st, "system", cg->cpuacct_stat.system);
830                         rrdset_done(st);
831                 }
832
833                 if(cg->cpuacct_usage.updated) {
834                         char id[RRD_ID_LENGTH_MAX + 1];
835                         unsigned int i;
836
837                         st = rrdset_find_bytype(type, "cpu_per_core");
838                         if(!st) {
839                                 snprintf(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->name);
840                                 st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
841
842                                 for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
843                                         snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
844                                         rrddim_add(st, id, NULL, 100, 1000000, RRDDIM_INCREMENTAL);
845                                 }
846                         }
847                         else rrdset_next(st);
848
849                         for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
850                                 snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
851                                 rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
852                         }
853                         rrdset_done(st);
854                 }
855
856                 if(cg->memory.updated) {
857                         if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
858                                 st = rrdset_find_bytype(type, "mem");
859                                 if(!st) {
860                                         snprintf(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->name);
861                                         st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
862                                                            RRDSET_TYPE_STACKED);
863
864                                         rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
865                                         rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
866                                         if(cg->memory.has_dirty_swap)
867                                                 rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
868                                         rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
869                                         rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
870                                 }
871                                 else rrdset_next(st);
872
873                                 rrddim_set(st, "cache", cg->memory.cache);
874                                 rrddim_set(st, "rss", cg->memory.rss);
875                                 if(cg->memory.has_dirty_swap)
876                                         rrddim_set(st, "swap", cg->memory.swap);
877                                 rrddim_set(st, "rss_huge", cg->memory.rss_huge);
878                                 rrddim_set(st, "mapped_file", cg->memory.mapped_file);
879                                 rrdset_done(st);
880                         }
881
882                         st = rrdset_find_bytype(type, "writeback");
883                         if(!st) {
884                                 snprintf(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->name);
885                                 st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
886                                                    update_every, RRDSET_TYPE_AREA);
887
888                                 if(cg->memory.has_dirty_swap)
889                                         rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
890                                 rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
891                         }
892                         else rrdset_next(st);
893
894                         if(cg->memory.has_dirty_swap)
895                                 rrddim_set(st, "dirty", cg->memory.dirty);
896                         rrddim_set(st, "writeback", cg->memory.writeback);
897                         rrdset_done(st);
898
899                         if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
900                                 st = rrdset_find_bytype(type, "mem_activity");
901                                 if(!st) {
902                                         snprintf(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->name);
903                                         st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
904                                                            40400, update_every, RRDSET_TYPE_LINE);
905
906                                         rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
907                                         rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
908                                 }
909                                 else rrdset_next(st);
910
911                                 rrddim_set(st, "pgpgin", cg->memory.pgpgin);
912                                 rrddim_set(st, "pgpgout", cg->memory.pgpgout);
913                                 rrdset_done(st);
914                         }
915
916                         if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
917                                 st = rrdset_find_bytype(type, "pgfaults");
918                                 if(!st) {
919                                         snprintf(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->name);
920                                         st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
921                                                            update_every, RRDSET_TYPE_LINE);
922
923                                         rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
924                                         rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
925                                 }
926                                 else rrdset_next(st);
927
928                                 rrddim_set(st, "pgfault", cg->memory.pgfault);
929                                 rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
930                                 rrdset_done(st);
931                         }
932                 }
933
934                 if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
935                         st = rrdset_find_bytype(type, "io");
936                         if(!st) {
937                                 snprintf(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->name);
938                                 st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
939                                                    update_every, RRDSET_TYPE_LINE);
940
941                                 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
942                                 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
943                         }
944                         else rrdset_next(st);
945
946                         rrddim_set(st, "read", cg->io_service_bytes.Read);
947                         rrddim_set(st, "write", cg->io_service_bytes.Write);
948                         rrdset_done(st);
949                 }
950
951                 if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
952                         st = rrdset_find_bytype(type, "serviced_ops");
953                         if(!st) {
954                                 snprintf(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->name);
955                                 st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
956                                                    update_every, RRDSET_TYPE_LINE);
957
958                                 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
959                                 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
960                         }
961                         else rrdset_next(st);
962
963                         rrddim_set(st, "read", cg->io_serviced.Read);
964                         rrddim_set(st, "write", cg->io_serviced.Write);
965                         rrdset_done(st);
966                 }
967
968                 if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
969                         st = rrdset_find_bytype(type, "io");
970                         if(!st) {
971                                 snprintf(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->name);
972                                 st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
973                                                    update_every, RRDSET_TYPE_LINE);
974
975                                 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
976                                 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
977                         }
978                         else rrdset_next(st);
979
980                         rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
981                         rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
982                         rrdset_done(st);
983                 }
984
985
986                 if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
987                         st = rrdset_find_bytype(type, "throttle_serviced_ops");
988                         if(!st) {
989                                 snprintf(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->name);
990                                 st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
991                                                    update_every, RRDSET_TYPE_LINE);
992
993                                 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
994                                 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
995                         }
996                         else rrdset_next(st);
997
998                         rrddim_set(st, "read", cg->throttle_io_serviced.Read);
999                         rrddim_set(st, "write", cg->throttle_io_serviced.Write);
1000                         rrdset_done(st);
1001                 }
1002
1003                 if(cg->io_queued.updated) {
1004                         st = rrdset_find_bytype(type, "queued_ops");
1005                         if(!st) {
1006                                 snprintf(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->name);
1007                                 st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
1008                                                    update_every, RRDSET_TYPE_LINE);
1009
1010                                 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
1011                                 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
1012                         }
1013                         else rrdset_next(st);
1014
1015                         rrddim_set(st, "read", cg->io_queued.Read);
1016                         rrddim_set(st, "write", cg->io_queued.Write);
1017                         rrdset_done(st);
1018                 }
1019
1020                 if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
1021                         st = rrdset_find_bytype(type, "merged_ops");
1022                         if(!st) {
1023                                 snprintf(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->name);
1024                                 st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
1025                                                    update_every, RRDSET_TYPE_LINE);
1026
1027                                 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1028                                 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1029                         }
1030                         else rrdset_next(st);
1031
1032                         rrddim_set(st, "read", cg->io_merged.Read);
1033                         rrddim_set(st, "write", cg->io_merged.Write);
1034                         rrdset_done(st);
1035                 }
1036         }
1037 }
1038
1039 // ----------------------------------------------------------------------------
1040 // cgroups main
1041
1042 int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
1043         static int cgroup_global_config_read = 0;
1044         static time_t last_run = 0;
1045         time_t now = time(NULL);
1046
1047         if(dt) {};
1048
1049         if(unlikely(!cgroup_global_config_read)) {
1050                 read_cgroup_plugin_configuration();
1051                 cgroup_global_config_read = 1;
1052         }
1053
1054         if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
1055                 find_all_cgroups();
1056                 last_run = now;
1057         }
1058
1059         read_all_cgroups(cgroup_root);
1060         update_cgroup_charts(update_every);
1061
1062         return 0;
1063 }