]> arthur.barton.de Git - netdata.git/blob - src/sys_fs_cgroup.c
Merge pull request #1998 from ktsaou/master
[netdata.git] / src / sys_fs_cgroup.c
1 #include "common.h"
2
3 // ----------------------------------------------------------------------------
4 // cgroup globals
5
6 #define CHART_PRIORITY_SYSTEMD_SERVICES 19000
7 #define CHART_PRIORITY_CONTAINERS       40000
8
9 static long system_page_size = 4096; // system will be queried via sysconf() in configuration()
10
11 static int cgroup_enable_cpuacct_stat = CONFIG_BOOLEAN_AUTO;
12 static int cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_AUTO;
13 static int cgroup_enable_memory = CONFIG_BOOLEAN_AUTO;
14 static int cgroup_enable_detailed_memory = CONFIG_BOOLEAN_AUTO;
15 static int cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_AUTO;
16 static int cgroup_enable_swap = CONFIG_BOOLEAN_AUTO;
17 static int cgroup_enable_blkio_io = CONFIG_BOOLEAN_AUTO;
18 static int cgroup_enable_blkio_ops = CONFIG_BOOLEAN_AUTO;
19 static int cgroup_enable_blkio_throttle_io = CONFIG_BOOLEAN_AUTO;
20 static int cgroup_enable_blkio_throttle_ops = CONFIG_BOOLEAN_AUTO;
21 static int cgroup_enable_blkio_merged_ops = CONFIG_BOOLEAN_AUTO;
22 static int cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_AUTO;
23
24 static int cgroup_enable_systemd_services = CONFIG_BOOLEAN_YES;
25 static int cgroup_enable_systemd_services_detailed_memory = CONFIG_BOOLEAN_NO;
26 static int cgroup_used_memory_without_cache = CONFIG_BOOLEAN_YES;
27
28 static int cgroup_search_in_devices = 1;
29
30 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
31 static int cgroup_check_for_new_every = 10;
32 static int cgroup_update_every = 1;
33
34 static int cgroup_recheck_zero_blkio_every_iterations = 10;
35 static int cgroup_recheck_zero_mem_failcnt_every_iterations = 10;
36 static int cgroup_recheck_zero_mem_detailed_every_iterations = 10;
37
38 static char *cgroup_cpuacct_base = NULL;
39 static char *cgroup_blkio_base = NULL;
40 static char *cgroup_memory_base = NULL;
41 static char *cgroup_devices_base = NULL;
42
43 static int cgroup_root_count = 0;
44 static int cgroup_root_max = 500;
45 static int cgroup_max_depth = 0;
46
47 static SIMPLE_PATTERN *enabled_cgroup_patterns = NULL;
48 static SIMPLE_PATTERN *enabled_cgroup_paths = NULL;
49 static SIMPLE_PATTERN *enabled_cgroup_renames = NULL;
50 static SIMPLE_PATTERN *systemd_services_cgroups = NULL;
51
52 static char *cgroups_rename_script = NULL;
53
54 static int cgroups_check = 0;
55
56 static uint32_t Read_hash = 0;
57 static uint32_t Write_hash = 0;
58 static uint32_t user_hash = 0;
59 static uint32_t system_hash = 0;
60
61 void read_cgroup_plugin_configuration() {
62     system_page_size = sysconf(_SC_PAGESIZE);
63
64     Read_hash = simple_hash("Read");
65     Write_hash = simple_hash("Write");
66     user_hash = simple_hash("user");
67     system_hash = simple_hash("system");
68
69     cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", localhost->rrd_update_every);
70     if(cgroup_update_every < localhost->rrd_update_every)
71         cgroup_update_every = localhost->rrd_update_every;
72
73     cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every);
74     if(cgroup_check_for_new_every < cgroup_update_every)
75         cgroup_check_for_new_every = cgroup_update_every;
76
77     cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat (total CPU)", cgroup_enable_cpuacct_stat);
78     cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage (per core CPU)", cgroup_enable_cpuacct_usage);
79
80     cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory (used mem including cache)", cgroup_enable_memory);
81     cgroup_enable_detailed_memory = config_get_boolean_ondemand("plugin:cgroups", "enable detailed memory", cgroup_enable_detailed_memory);
82     cgroup_enable_memory_failcnt = config_get_boolean_ondemand("plugin:cgroups", "enable memory limits fail count", cgroup_enable_memory_failcnt);
83     cgroup_enable_swap = config_get_boolean_ondemand("plugin:cgroups", "enable swap memory", cgroup_enable_swap);
84
85     cgroup_enable_blkio_io = config_get_boolean_ondemand("plugin:cgroups", "enable blkio bandwidth", cgroup_enable_blkio_io);
86     cgroup_enable_blkio_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio operations", cgroup_enable_blkio_ops);
87     cgroup_enable_blkio_throttle_io = config_get_boolean_ondemand("plugin:cgroups", "enable blkio throttle bandwidth", cgroup_enable_blkio_throttle_io);
88     cgroup_enable_blkio_throttle_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio throttle operations", cgroup_enable_blkio_throttle_ops);
89     cgroup_enable_blkio_queued_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio queued operations", cgroup_enable_blkio_queued_ops);
90     cgroup_enable_blkio_merged_ops = config_get_boolean_ondemand("plugin:cgroups", "enable blkio merged operations", cgroup_enable_blkio_merged_ops);
91
92     cgroup_recheck_zero_blkio_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero blkio every iterations", cgroup_recheck_zero_blkio_every_iterations);
93     cgroup_recheck_zero_mem_failcnt_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero memory failcnt every iterations", cgroup_recheck_zero_mem_failcnt_every_iterations);
94     cgroup_recheck_zero_mem_detailed_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero detailed memory every iterations", cgroup_recheck_zero_mem_detailed_every_iterations);
95
96     cgroup_enable_systemd_services = config_get_boolean("plugin:cgroups", "enable systemd services", cgroup_enable_systemd_services);
97     cgroup_enable_systemd_services_detailed_memory = config_get_boolean("plugin:cgroups", "enable systemd services detailed memory", cgroup_enable_systemd_services_detailed_memory);
98     cgroup_used_memory_without_cache = config_get_boolean("plugin:cgroups", "report used memory without cache", cgroup_used_memory_without_cache);
99
100     char filename[FILENAME_MAX + 1], *s;
101     struct mountinfo *mi, *root = mountinfo_read(0);
102
103     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
104     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
105     if(!mi) {
106         error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
107         s = "/sys/fs/cgroup/cpuacct";
108     }
109     else s = mi->mount_point;
110     snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, s);
111     cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
112
113     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
114     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
115     if(!mi) {
116         error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
117         s = "/sys/fs/cgroup/blkio";
118     }
119     else s = mi->mount_point;
120     snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, s);
121     cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
122
123     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
124     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
125     if(!mi) {
126         error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
127         s = "/sys/fs/cgroup/memory";
128     }
129     else s = mi->mount_point;
130     snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, s);
131     cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
132
133     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
134     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
135     if(!mi) {
136         error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
137         s = "/sys/fs/cgroup/devices";
138     }
139     else s = mi->mount_point;
140     snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, s);
141     cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
142
143     cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
144     cgroup_max_depth = (int)config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
145
146     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);
147
148     enabled_cgroup_patterns = simple_pattern_create(
149             config_get("plugin:cgroups", "enable by default cgroups matching",
150             // ----------------------------------------------------------------
151
152                     " !*/init.scope "                      // ignore init.scope
153                     " *.scope "                            // we need all *.scope for sure
154
155             // ----------------------------------------------------------------
156
157                     " !*/vcpu* "                           // libvirtd adds these sub-cgroups
158                     " !*/emulator "                        // libvirtd adds these sub-cgroups
159                     " !*.mount "
160                     " !*.partition "
161                     " !*.service "
162                     " !*.slice "
163                     " !*.swap "
164                     " !*.user "
165                     " !/ "
166                     " !/docker "
167                     " !/libvirt "
168                     " !/lxc "
169                     " !/lxc/*/ns "                         //  #1397
170                     " !/machine "
171                     " !/qemu "
172                     " !/system "
173                     " !/systemd "
174                     " !/user "
175                     " * "                                  // enable anything else
176             ), SIMPLE_PATTERN_EXACT);
177
178     enabled_cgroup_paths = simple_pattern_create(
179             config_get("plugin:cgroups", "search for cgroups in subpaths matching",
180                     " !*/init.scope "                      // ignore init.scope
181                     " !*-qemu "                            //  #345
182                     " !/init.scope "
183                     " !/system "
184                     " !/systemd "
185                     " !/user "
186                     " !/user.slice "
187                     " * "
188             ), SIMPLE_PATTERN_EXACT);
189
190     snprintfz(filename, FILENAME_MAX, "%s/cgroup-name.sh", netdata_configured_plugins_dir);
191     cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", filename);
192
193     enabled_cgroup_renames = simple_pattern_create(
194             config_get("plugin:cgroups", "run script to rename cgroups matching",
195                     " *.scope "
196                     " *docker* "
197                     " *lxc* "
198                     " *qemu* "
199                     " !/ "
200                     " !*.mount "
201                     " !*.partition "
202                     " !*.service "
203                     " !*.slice "
204                     " !*.swap "
205                     " !*.user "
206                     " * "
207             ), SIMPLE_PATTERN_EXACT);
208
209     if(cgroup_enable_systemd_services) {
210         systemd_services_cgroups = simple_pattern_create(
211                 config_get("plugin:cgroups", "cgroups to match as systemd services",
212                         " !/system.slice/*/*.service "
213                         " /system.slice/*.service "
214                 ), SIMPLE_PATTERN_EXACT);
215     }
216
217     mountinfo_free(root);
218 }
219
220 // ----------------------------------------------------------------------------
221 // cgroup objects
222
223 struct blkio {
224     int updated;
225     int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
226     int delay_counter;
227
228     char *filename;
229
230     unsigned long long Read;
231     unsigned long long Write;
232 /*
233     unsigned long long Sync;
234     unsigned long long Async;
235     unsigned long long Total;
236 */
237 };
238
239 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
240 struct memory {
241     ARL_BASE *arl_base;
242     ARL_ENTRY *arl_dirty;
243     ARL_ENTRY *arl_swap;
244
245     int updated_detailed;
246     int updated_usage_in_bytes;
247     int updated_msw_usage_in_bytes;
248     int updated_failcnt;
249
250     int enabled_detailed;           // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
251     int enabled_usage_in_bytes;     // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
252     int enabled_msw_usage_in_bytes; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
253     int enabled_failcnt;            // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
254
255     int delay_counter_detailed;
256     int delay_counter_failcnt;
257
258     char *filename_detailed;
259     char *filename_usage_in_bytes;
260     char *filename_msw_usage_in_bytes;
261     char *filename_failcnt;
262
263     int detailed_has_dirty;
264     int detailed_has_swap;
265
266     // detailed metrics
267     unsigned long long cache;
268     unsigned long long rss;
269     unsigned long long rss_huge;
270     unsigned long long mapped_file;
271     unsigned long long writeback;
272     unsigned long long dirty;
273     unsigned long long swap;
274     unsigned long long pgpgin;
275     unsigned long long pgpgout;
276     unsigned long long pgfault;
277     unsigned long long pgmajfault;
278 /*
279     unsigned long long inactive_anon;
280     unsigned long long active_anon;
281     unsigned long long inactive_file;
282     unsigned long long active_file;
283     unsigned long long unevictable;
284     unsigned long long hierarchical_memory_limit;
285     unsigned long long total_cache;
286     unsigned long long total_rss;
287     unsigned long long total_rss_huge;
288     unsigned long long total_mapped_file;
289     unsigned long long total_writeback;
290     unsigned long long total_dirty;
291     unsigned long long total_swap;
292     unsigned long long total_pgpgin;
293     unsigned long long total_pgpgout;
294     unsigned long long total_pgfault;
295     unsigned long long total_pgmajfault;
296     unsigned long long total_inactive_anon;
297     unsigned long long total_active_anon;
298     unsigned long long total_inactive_file;
299     unsigned long long total_active_file;
300     unsigned long long total_unevictable;
301 */
302
303     // single file metrics
304     unsigned long long usage_in_bytes;
305     unsigned long long msw_usage_in_bytes;
306     unsigned long long failcnt;
307 };
308
309 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
310 struct cpuacct_stat {
311     int updated;
312     int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
313
314     char *filename;
315
316     unsigned long long user;
317     unsigned long long system;
318 };
319
320 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
321 struct cpuacct_usage {
322     int updated;
323     int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
324
325     char *filename;
326
327     unsigned int cpus;
328     unsigned long long *cpu_percpu;
329 };
330
331 #define CGROUP_OPTIONS_DISABLED_DUPLICATE   0x00000001
332 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
333
334 struct cgroup {
335     uint32_t options;
336
337     char available;      // found in the filesystem
338     char enabled;        // enabled in the config
339
340     char *id;
341     uint32_t hash;
342
343     char *chart_id;
344     uint32_t hash_chart;
345
346     char *chart_title;
347
348     struct cpuacct_stat cpuacct_stat;
349     struct cpuacct_usage cpuacct_usage;
350
351     struct memory memory;
352
353     struct blkio io_service_bytes;              // bytes
354     struct blkio io_serviced;                   // operations
355
356     struct blkio throttle_io_service_bytes;     // bytes
357     struct blkio throttle_io_serviced;          // operations
358
359     struct blkio io_merged;                     // operations
360     struct blkio io_queued;                     // operations
361
362     // per cgroup charts
363     RRDSET *st_cpu;
364     RRDSET *st_cpu_per_core;
365     RRDSET *st_mem;
366     RRDSET *st_writeback;
367     RRDSET *st_mem_activity;
368     RRDSET *st_pgfaults;
369     RRDSET *st_mem_usage;
370     RRDSET *st_mem_failcnt;
371     RRDSET *st_io;
372     RRDSET *st_serviced_ops;
373     RRDSET *st_throttle_io;
374     RRDSET *st_throttle_serviced_ops;
375     RRDSET *st_queued_ops;
376     RRDSET *st_merged_ops;
377
378     // services
379     RRDDIM *rd_cpu;
380     RRDDIM *rd_mem_usage;
381     RRDDIM *rd_mem_failcnt;
382     RRDDIM *rd_swap_usage;
383
384     RRDDIM *rd_mem_detailed_cache;
385     RRDDIM *rd_mem_detailed_rss;
386     RRDDIM *rd_mem_detailed_mapped;
387     RRDDIM *rd_mem_detailed_writeback;
388     RRDDIM *rd_mem_detailed_pgpgin;
389     RRDDIM *rd_mem_detailed_pgpgout;
390     RRDDIM *rd_mem_detailed_pgfault;
391     RRDDIM *rd_mem_detailed_pgmajfault;
392
393     RRDDIM *rd_io_service_bytes_read;
394     RRDDIM *rd_io_serviced_read;
395     RRDDIM *rd_throttle_io_read;
396     RRDDIM *rd_throttle_io_serviced_read;
397     RRDDIM *rd_io_queued_read;
398     RRDDIM *rd_io_merged_read;
399
400     RRDDIM *rd_io_service_bytes_write;
401     RRDDIM *rd_io_serviced_write;
402     RRDDIM *rd_throttle_io_write;
403     RRDDIM *rd_throttle_io_serviced_write;
404     RRDDIM *rd_io_queued_write;
405     RRDDIM *rd_io_merged_write;
406
407     struct cgroup *next;
408
409 } *cgroup_root = NULL;
410
411 // ----------------------------------------------------------------------------
412 // read values from /sys
413
414 static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
415     static procfile *ff = NULL;
416
417     if(likely(cp->filename)) {
418         ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
419         if(unlikely(!ff)) {
420             cp->updated = 0;
421             cgroups_check = 1;
422             return;
423         }
424
425         ff = procfile_readall(ff);
426         if(unlikely(!ff)) {
427             cp->updated = 0;
428             cgroups_check = 1;
429             return;
430         }
431
432         unsigned long i, lines = procfile_lines(ff);
433
434         if(unlikely(lines < 1)) {
435             error("File '%s' should have 1+ lines.", cp->filename);
436             cp->updated = 0;
437             return;
438         }
439
440         for(i = 0; i < lines ; i++) {
441             char *s = procfile_lineword(ff, i, 0);
442             uint32_t hash = simple_hash(s);
443
444             if(unlikely(hash == user_hash && !strcmp(s, "user")))
445                 cp->user = str2ull(procfile_lineword(ff, i, 1));
446
447             else if(unlikely(hash == system_hash && !strcmp(s, "system")))
448                 cp->system = str2ull(procfile_lineword(ff, i, 1));
449         }
450
451         cp->updated = 1;
452
453         if(unlikely(cp->enabled == CONFIG_BOOLEAN_AUTO && (cp->user || cp->system)))
454             cp->enabled = CONFIG_BOOLEAN_YES;
455     }
456 }
457
458 static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
459     static procfile *ff = NULL;
460
461     if(likely(ca->filename)) {
462         ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
463         if(unlikely(!ff)) {
464             ca->updated = 0;
465             cgroups_check = 1;
466             return;
467         }
468
469         ff = procfile_readall(ff);
470         if(unlikely(!ff)) {
471             ca->updated = 0;
472             cgroups_check = 1;
473             return;
474         }
475
476         if(unlikely(procfile_lines(ff) < 1)) {
477             error("File '%s' should have 1+ lines but has %zu.", ca->filename, procfile_lines(ff));
478             ca->updated = 0;
479             return;
480         }
481
482         unsigned long i = procfile_linewords(ff, 0);
483         if(unlikely(i == 0)) {
484             ca->updated = 0;
485             return;
486         }
487
488         // we may have 1 more CPU reported
489         while(i > 0) {
490             char *s = procfile_lineword(ff, 0, i - 1);
491             if(!*s) i--;
492             else break;
493         }
494
495         if(unlikely(i != ca->cpus)) {
496             freez(ca->cpu_percpu);
497             ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
498             ca->cpus = (unsigned int)i;
499         }
500
501         unsigned long long total = 0;
502         for(i = 0; i < ca->cpus ;i++) {
503             unsigned long long n = str2ull(procfile_lineword(ff, 0, i));
504             ca->cpu_percpu[i] = n;
505             total += n;
506         }
507
508         ca->updated = 1;
509
510         if(unlikely(ca->enabled == CONFIG_BOOLEAN_AUTO && total))
511             ca->enabled = CONFIG_BOOLEAN_YES;
512     }
513 }
514
515 static inline void cgroup_read_blkio(struct blkio *io) {
516     static procfile *ff = NULL;
517
518     if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO && io->delay_counter > 0)) {
519         io->delay_counter--;
520         return;
521     }
522
523     if(likely(io->filename)) {
524         ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
525         if(unlikely(!ff)) {
526             io->updated = 0;
527             cgroups_check = 1;
528             return;
529         }
530
531         ff = procfile_readall(ff);
532         if(unlikely(!ff)) {
533             io->updated = 0;
534             cgroups_check = 1;
535             return;
536         }
537
538         unsigned long i, lines = procfile_lines(ff);
539
540         if(unlikely(lines < 1)) {
541             error("File '%s' should have 1+ lines.", io->filename);
542             io->updated = 0;
543             return;
544         }
545
546         io->Read = 0;
547         io->Write = 0;
548 /*
549         io->Sync = 0;
550         io->Async = 0;
551         io->Total = 0;
552 */
553
554         for(i = 0; i < lines ; i++) {
555             char *s = procfile_lineword(ff, i, 1);
556             uint32_t hash = simple_hash(s);
557
558             if(unlikely(hash == Read_hash && !strcmp(s, "Read")))
559                 io->Read += str2ull(procfile_lineword(ff, i, 2));
560
561             else if(unlikely(hash == Write_hash && !strcmp(s, "Write")))
562                 io->Write += str2ull(procfile_lineword(ff, i, 2));
563
564 /*
565             else if(unlikely(hash == Sync_hash && !strcmp(s, "Sync")))
566                 io->Sync += str2ull(procfile_lineword(ff, i, 2));
567
568             else if(unlikely(hash == Async_hash && !strcmp(s, "Async")))
569                 io->Async += str2ull(procfile_lineword(ff, i, 2));
570
571             else if(unlikely(hash == Total_hash && !strcmp(s, "Total")))
572                 io->Total += str2ull(procfile_lineword(ff, i, 2));
573 */
574         }
575
576         io->updated = 1;
577
578         if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO)) {
579             if(unlikely(io->Read || io->Write))
580                 io->enabled = CONFIG_BOOLEAN_YES;
581             else
582                 io->delay_counter = cgroup_recheck_zero_blkio_every_iterations;
583         }
584     }
585 }
586
587 static inline void cgroup_read_memory(struct memory *mem) {
588     static procfile *ff = NULL;
589
590     // read detailed ram usage
591     if(likely(mem->filename_detailed)) {
592         if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO && mem->delay_counter_detailed > 0)) {
593             mem->delay_counter_detailed--;
594             goto memory_next;
595         }
596
597         ff = procfile_reopen(ff, mem->filename_detailed, NULL, PROCFILE_FLAG_DEFAULT);
598         if(unlikely(!ff)) {
599             mem->updated_detailed = 0;
600             cgroups_check = 1;
601             goto memory_next;
602         }
603
604         ff = procfile_readall(ff);
605         if(unlikely(!ff)) {
606             mem->updated_detailed = 0;
607             cgroups_check = 1;
608             goto memory_next;
609         }
610
611         unsigned long i, lines = procfile_lines(ff);
612
613         if(unlikely(lines < 1)) {
614             error("File '%s' should have 1+ lines.", mem->filename_detailed);
615             mem->updated_detailed = 0;
616             goto memory_next;
617         }
618
619         if(unlikely(!mem->arl_base)) {
620             mem->arl_base = arl_create("cgroup/memory", NULL, 60);
621
622             arl_expect(mem->arl_base, "cache", &mem->cache);
623             arl_expect(mem->arl_base, "rss", &mem->rss);
624             arl_expect(mem->arl_base, "rss_huge", &mem->rss_huge);
625             arl_expect(mem->arl_base, "mapped_file", &mem->mapped_file);
626             arl_expect(mem->arl_base, "writeback", &mem->writeback);
627             mem->arl_dirty = arl_expect(mem->arl_base, "dirty", &mem->dirty);
628             mem->arl_swap  = arl_expect(mem->arl_base, "swap", &mem->swap);
629             arl_expect(mem->arl_base, "pgpgin", &mem->pgpgin);
630             arl_expect(mem->arl_base, "pgpgout", &mem->pgpgout);
631             arl_expect(mem->arl_base, "pgfault", &mem->pgfault);
632             arl_expect(mem->arl_base, "pgmajfault", &mem->pgmajfault);
633         }
634
635         arl_begin(mem->arl_base);
636
637         for(i = 0; i < lines ; i++) {
638             if(arl_check(mem->arl_base,
639                     procfile_lineword(ff, i, 0),
640                     procfile_lineword(ff, i, 1))) break;
641         }
642
643         if(unlikely(mem->arl_dirty->flags & ARL_ENTRY_FLAG_FOUND))
644             mem->detailed_has_dirty = 1;
645
646         if(unlikely(mem->arl_swap->flags & ARL_ENTRY_FLAG_FOUND))
647             mem->detailed_has_swap = 1;
648
649         // 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);
650
651         mem->updated_detailed = 1;
652
653         if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO)) {
654             if(mem->cache || mem->dirty || mem->rss || mem->rss_huge || mem->mapped_file || mem->writeback || mem->swap || mem->pgpgin || mem->pgpgout || mem->pgfault || mem->pgmajfault)
655                 mem->enabled_detailed = CONFIG_BOOLEAN_YES;
656             else
657                 mem->delay_counter_detailed = cgroup_recheck_zero_mem_detailed_every_iterations;
658         }
659     }
660
661 memory_next:
662
663     // read usage_in_bytes
664     if(likely(mem->filename_usage_in_bytes)) {
665         mem->updated_usage_in_bytes = !read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes);
666         if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->usage_in_bytes))
667             mem->enabled_usage_in_bytes = CONFIG_BOOLEAN_YES;
668     }
669
670     // read msw_usage_in_bytes
671     if(likely(mem->filename_msw_usage_in_bytes)) {
672         mem->updated_msw_usage_in_bytes = !read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes);
673         if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->msw_usage_in_bytes))
674             mem->enabled_msw_usage_in_bytes = CONFIG_BOOLEAN_YES;
675     }
676
677     // read failcnt
678     if(likely(mem->filename_failcnt)) {
679         if(unlikely(mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO && mem->delay_counter_failcnt > 0)) {
680             mem->updated_failcnt = 0;
681             mem->delay_counter_failcnt--;
682         }
683         else {
684             mem->updated_failcnt = !read_single_number_file(mem->filename_failcnt, &mem->failcnt);
685             if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO)) {
686                 if(unlikely(!mem->failcnt))
687                     mem->delay_counter_failcnt = cgroup_recheck_zero_mem_failcnt_every_iterations;
688                 else
689                     mem->enabled_failcnt = CONFIG_BOOLEAN_YES;
690             }
691         }
692     }
693 }
694
695 static inline void cgroup_read(struct cgroup *cg) {
696     debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
697
698     cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
699     cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
700     cgroup_read_memory(&cg->memory);
701     cgroup_read_blkio(&cg->io_service_bytes);
702     cgroup_read_blkio(&cg->io_serviced);
703     cgroup_read_blkio(&cg->throttle_io_service_bytes);
704     cgroup_read_blkio(&cg->throttle_io_serviced);
705     cgroup_read_blkio(&cg->io_merged);
706     cgroup_read_blkio(&cg->io_queued);
707 }
708
709 static inline void read_all_cgroups(struct cgroup *root) {
710     debug(D_CGROUP, "reading metrics for all cgroups");
711
712     struct cgroup *cg;
713
714     for(cg = root; cg ; cg = cg->next)
715         if(cg->enabled && cg->available)
716             cgroup_read(cg);
717 }
718
719 // ----------------------------------------------------------------------------
720 // add/remove/find cgroup objects
721
722 #define CGROUP_CHARTID_LINE_MAX 1024
723
724 static inline char *cgroup_title_strdupz(const char *s) {
725     if(!s || !*s) s = "/";
726
727     if(*s == '/' && s[1] != '\0') s++;
728
729     char *r = strdupz(s);
730     netdata_fix_chart_name(r);
731
732     return r;
733 }
734
735 static inline char *cgroup_chart_id_strdupz(const char *s) {
736     if(!s || !*s) s = "/";
737
738     if(*s == '/' && s[1] != '\0') s++;
739
740     char *r = strdupz(s);
741     netdata_fix_chart_id(r);
742
743     return r;
744 }
745
746 static inline void cgroup_get_chart_name(struct cgroup *cg) {
747     debug(D_CGROUP, "looking for the name of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title);
748
749     pid_t cgroup_pid;
750     char buffer[CGROUP_CHARTID_LINE_MAX + 1];
751
752     snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", cgroups_rename_script, cg->chart_id);
753
754     debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
755     FILE *fp = mypopen(buffer, &cgroup_pid);
756     if(fp) {
757         // debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
758         char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
759         // debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
760         mypclose(fp, cgroup_pid);
761         // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
762
763         if(s && *s && *s != '\n') {
764             debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
765
766             trim(s);
767
768             freez(cg->chart_title);
769             cg->chart_title = cgroup_title_strdupz(s);
770
771             freez(cg->chart_id);
772             cg->chart_id = cgroup_chart_id_strdupz(s);
773             cg->hash_chart = simple_hash(cg->chart_id);
774         }
775     }
776     else
777         error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
778 }
779
780 static inline struct cgroup *cgroup_add(const char *id) {
781     if(!id || !*id) id = "/";
782     debug(D_CGROUP, "adding to list, cgroup with id '%s'", id);
783
784     if(cgroup_root_count >= cgroup_root_max) {
785         info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
786         return NULL;
787     }
788
789     int def = simple_pattern_matches(enabled_cgroup_patterns, id)?cgroup_enable_new_cgroups_detected_at_runtime:0;
790     struct cgroup *cg = callocz(1, sizeof(struct cgroup));
791
792     cg->id = strdupz(id);
793     cg->hash = simple_hash(cg->id);
794
795     cg->chart_title = cgroup_title_strdupz(id);
796
797     cg->chart_id = cgroup_chart_id_strdupz(id);
798     cg->hash_chart = simple_hash(cg->chart_id);
799
800     if(!cgroup_root)
801         cgroup_root = cg;
802     else {
803         // append it
804         struct cgroup *e;
805         for(e = cgroup_root; e->next ;e = e->next) ;
806         e->next = cg;
807     }
808
809     cgroup_root_count++;
810
811     // fix the chart_id and title by calling the external script
812     if(simple_pattern_matches(enabled_cgroup_renames, cg->id)) {
813
814         cgroup_get_chart_name(cg);
815
816         debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
817     }
818     else
819         debug(D_CGROUP, "cgroup '%s' will not be renamed - it matches the list of disabled cgroup renames (will be shown as '%s')", cg->id, cg->chart_id);
820
821     int user_configurable = 1;
822
823     // check if this cgroup should be a systemd service
824     if(cgroup_enable_systemd_services) {
825         if(simple_pattern_matches(systemd_services_cgroups, cg->id) ||
826                 simple_pattern_matches(systemd_services_cgroups, cg->chart_id)) {
827             debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
828
829             char buffer[CGROUP_CHARTID_LINE_MAX + 1];
830             cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
831
832             strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
833             char *s = buffer;
834
835             //freez(cg->chart_id);
836             //cg->chart_id = cgroup_chart_id_strdupz(s);
837             //cg->hash_chart = simple_hash(cg->chart_id);
838
839             // skip to the last slash
840             size_t len = strlen(s);
841             while(len--) if(unlikely(s[len] == '/')) break;
842             if(len) s = &s[len + 1];
843
844             // remove extension
845             len = strlen(s);
846             while(len--) if(unlikely(s[len] == '.')) break;
847             if(len) s[len] = '\0';
848
849             freez(cg->chart_title);
850             cg->chart_title = cgroup_title_strdupz(s);
851
852             cg->enabled = 1;
853             user_configurable = 0;
854
855             debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
856         }
857         else
858             debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') does not match systemd services groups", cg->id, cg->chart_id, cg->chart_title);
859     }
860
861     if(user_configurable) {
862         // allow the user to enable/disable this individualy
863         char option[FILENAME_MAX + 1];
864         snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
865         cg->enabled = (char) config_get_boolean("plugin:cgroups", option, def);
866     }
867
868     // detect duplicate cgroups
869     if(cg->enabled) {
870         struct cgroup *t;
871         for (t = cgroup_root; t; t = t->next) {
872             if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
873                 if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
874                     error("Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
875                           cg->chart_id, t->id, cg->id, t->id);
876                     debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
877                           cg->chart_id, t->id, cg->id, t->id);
878                     t->enabled = 0;
879                     t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
880                 }
881                 else {
882                     error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
883                           cg->chart_id, t->id, cg->id);
884                     debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
885                           cg->chart_id, t->id, cg->id);
886                     cg->enabled = 0;
887                     cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
888                 }
889
890                 break;
891             }
892         }
893     }
894
895     debug(D_CGROUP, "ADDED CGROUP: '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
896
897     return cg;
898 }
899
900 static inline void cgroup_free(struct cgroup *cg) {
901     debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
902
903     if(cg->st_cpu)                   rrdset_flag_set(cg->st_cpu,                   RRDSET_FLAG_OBSOLETE);
904     if(cg->st_cpu_per_core)          rrdset_flag_set(cg->st_cpu_per_core,          RRDSET_FLAG_OBSOLETE);
905     if(cg->st_mem)                   rrdset_flag_set(cg->st_mem,                   RRDSET_FLAG_OBSOLETE);
906     if(cg->st_writeback)             rrdset_flag_set(cg->st_writeback,             RRDSET_FLAG_OBSOLETE);
907     if(cg->st_mem_activity)          rrdset_flag_set(cg->st_mem_activity,          RRDSET_FLAG_OBSOLETE);
908     if(cg->st_pgfaults)              rrdset_flag_set(cg->st_pgfaults,              RRDSET_FLAG_OBSOLETE);
909     if(cg->st_mem_usage)             rrdset_flag_set(cg->st_mem_usage,             RRDSET_FLAG_OBSOLETE);
910     if(cg->st_mem_failcnt)           rrdset_flag_set(cg->st_mem_failcnt,           RRDSET_FLAG_OBSOLETE);
911     if(cg->st_io)                    rrdset_flag_set(cg->st_io,                    RRDSET_FLAG_OBSOLETE);
912     if(cg->st_serviced_ops)          rrdset_flag_set(cg->st_serviced_ops,          RRDSET_FLAG_OBSOLETE);
913     if(cg->st_throttle_io)           rrdset_flag_set(cg->st_throttle_io,           RRDSET_FLAG_OBSOLETE);
914     if(cg->st_throttle_serviced_ops) rrdset_flag_set(cg->st_throttle_serviced_ops, RRDSET_FLAG_OBSOLETE);
915     if(cg->st_queued_ops)            rrdset_flag_set(cg->st_queued_ops,            RRDSET_FLAG_OBSOLETE);
916     if(cg->st_merged_ops)            rrdset_flag_set(cg->st_merged_ops,            RRDSET_FLAG_OBSOLETE);
917
918     freez(cg->cpuacct_usage.cpu_percpu);
919
920     freez(cg->cpuacct_stat.filename);
921     freez(cg->cpuacct_usage.filename);
922
923     arl_free(cg->memory.arl_base);
924     freez(cg->memory.filename_detailed);
925     freez(cg->memory.filename_failcnt);
926     freez(cg->memory.filename_usage_in_bytes);
927     freez(cg->memory.filename_msw_usage_in_bytes);
928
929     freez(cg->io_service_bytes.filename);
930     freez(cg->io_serviced.filename);
931
932     freez(cg->throttle_io_service_bytes.filename);
933     freez(cg->throttle_io_serviced.filename);
934
935     freez(cg->io_merged.filename);
936     freez(cg->io_queued.filename);
937
938     freez(cg->id);
939     freez(cg->chart_id);
940     freez(cg->chart_title);
941
942     freez(cg);
943
944     cgroup_root_count--;
945 }
946
947 // find if a given cgroup exists
948 static inline struct cgroup *cgroup_find(const char *id) {
949     debug(D_CGROUP, "searching for cgroup '%s'", id);
950
951     uint32_t hash = simple_hash(id);
952
953     struct cgroup *cg;
954     for(cg = cgroup_root; cg ; cg = cg->next) {
955         if(hash == cg->hash && strcmp(id, cg->id) == 0)
956             break;
957     }
958
959     debug(D_CGROUP, "cgroup '%s' %s in memory", id, (cg)?"found":"not found");
960     return cg;
961 }
962
963 // ----------------------------------------------------------------------------
964 // detect running cgroups
965
966 // callback for find_file_in_subdirs()
967 static inline void found_subdir_in_dir(const char *dir) {
968     debug(D_CGROUP, "examining cgroup dir '%s'", dir);
969
970     struct cgroup *cg = cgroup_find(dir);
971     if(!cg) {
972         if(*dir && cgroup_max_depth > 0) {
973             int depth = 0;
974             const char *s;
975
976             for(s = dir; *s ;s++)
977                 if(unlikely(*s == '/'))
978                     depth++;
979
980             if(depth > cgroup_max_depth) {
981                 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
982                 return;
983             }
984         }
985         // debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
986         cg = cgroup_add(dir);
987     }
988
989     if(cg) cg->available = 1;
990 }
991
992 static inline int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
993     if(!this) this = base;
994     debug(D_CGROUP, "searching for directories in '%s' (base '%s')", this?this:"", base);
995
996     size_t dirlen = strlen(this), baselen = strlen(base);
997
998     int ret = -1;
999     int enabled = -1;
1000
1001     const char *relative_path = &this[baselen];
1002     if(!*relative_path) relative_path = "/";
1003
1004     DIR *dir = opendir(this);
1005     if(!dir) {
1006         error("Cannot read cgroups directory '%s'", base);
1007         return ret;
1008     }
1009     ret = 1;
1010
1011     callback(relative_path);
1012
1013     struct dirent *de = NULL;
1014     while((de = readdir(dir))) {
1015         if(de->d_type == DT_DIR
1016             && (
1017                 (de->d_name[0] == '.' && de->d_name[1] == '\0')
1018                 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
1019                 ))
1020             continue;
1021
1022         if(de->d_type == DT_DIR) {
1023             if(enabled == -1) {
1024                 const char *r = relative_path;
1025                 if(*r == '\0') r = "/";
1026
1027                 // do not decent in directories we are not interested
1028                 int def = simple_pattern_matches(enabled_cgroup_paths, r);
1029
1030                 // we check for this option here
1031                 // so that the config will not have settings
1032                 // for leaf directories
1033                 char option[FILENAME_MAX + 1];
1034                 snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
1035                 option[FILENAME_MAX] = '\0';
1036                 enabled = config_get_boolean("plugin:cgroups", option, def);
1037             }
1038
1039             if(enabled) {
1040                 char *s = mallocz(dirlen + strlen(de->d_name) + 2);
1041                 strcpy(s, this);
1042                 strcat(s, "/");
1043                 strcat(s, de->d_name);
1044                 int ret2 = find_dir_in_subdirs(base, s, callback);
1045                 if(ret2 > 0) ret += ret2;
1046                 freez(s);
1047             }
1048         }
1049     }
1050
1051     closedir(dir);
1052     return ret;
1053 }
1054
1055 static inline void mark_all_cgroups_as_not_available() {
1056     debug(D_CGROUP, "marking all cgroups as not available");
1057
1058     struct cgroup *cg;
1059
1060     // mark all as not available
1061     for(cg = cgroup_root; cg ; cg = cg->next) {
1062         cg->available = 0;
1063     }
1064 }
1065
1066 static inline void cleanup_all_cgroups() {
1067     struct cgroup *cg = cgroup_root, *last = NULL;
1068
1069     for(; cg ;) {
1070         if(!cg->available) {
1071             // enable the first duplicate cgroup
1072             {
1073                 struct cgroup *t;
1074                 for(t = cgroup_root; t ; t = t->next) {
1075                     if(t != cg && t->available && !t->enabled && t->options & CGROUP_OPTIONS_DISABLED_DUPLICATE && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
1076                         debug(D_CGROUP, "Enabling duplicate of cgroup '%s' with id '%s', because the original with id '%s' stopped.", t->chart_id, t->id, cg->id);
1077                         t->enabled = 1;
1078                         t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1079                         break;
1080                     }
1081                 }
1082             }
1083
1084             if(!last)
1085                 cgroup_root = cg->next;
1086             else
1087                 last->next = cg->next;
1088
1089             cgroup_free(cg);
1090
1091             if(!last)
1092                 cg = cgroup_root;
1093             else
1094                 cg = last->next;
1095         }
1096         else {
1097             last = cg;
1098             cg = cg->next;
1099         }
1100     }
1101 }
1102
1103 static inline void find_all_cgroups() {
1104     debug(D_CGROUP, "searching for cgroups");
1105
1106     mark_all_cgroups_as_not_available();
1107
1108     if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
1109         if(find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
1110             cgroup_enable_cpuacct_stat =
1111             cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_NO;
1112             error("disabled CGROUP cpu statistics.");
1113         }
1114     }
1115
1116     if(cgroup_enable_blkio_io || cgroup_enable_blkio_ops || cgroup_enable_blkio_throttle_io || cgroup_enable_blkio_throttle_ops || cgroup_enable_blkio_merged_ops || cgroup_enable_blkio_queued_ops) {
1117         if(find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
1118             cgroup_enable_blkio_io =
1119             cgroup_enable_blkio_ops =
1120             cgroup_enable_blkio_throttle_io =
1121             cgroup_enable_blkio_throttle_ops =
1122             cgroup_enable_blkio_merged_ops =
1123             cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_NO;
1124             error("disabled CGROUP blkio statistics.");
1125         }
1126     }
1127
1128     if(cgroup_enable_memory || cgroup_enable_detailed_memory || cgroup_enable_swap || cgroup_enable_memory_failcnt) {
1129         if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
1130             cgroup_enable_memory =
1131             cgroup_enable_detailed_memory =
1132             cgroup_enable_swap =
1133             cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_NO;
1134             error("disabled CGROUP memory statistics.");
1135         }
1136     }
1137
1138     if(cgroup_search_in_devices) {
1139         if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
1140             cgroup_search_in_devices = 0;
1141             error("disabled CGROUP devices statistics.");
1142         }
1143     }
1144
1145     // remove any non-existing cgroups
1146     cleanup_all_cgroups();
1147
1148     struct cgroup *cg;
1149     struct stat buf;
1150     for(cg = cgroup_root; cg ; cg = cg->next) {
1151         // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
1152
1153         if(unlikely(!cg->available))
1154             continue;
1155
1156         debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
1157
1158         // check for newly added cgroups
1159         // and update the filenames they read
1160         char filename[FILENAME_MAX + 1];
1161         if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
1162             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
1163             if(likely(stat(filename, &buf) != -1)) {
1164                 cg->cpuacct_stat.filename = strdupz(filename);
1165                 cg->cpuacct_stat.enabled = cgroup_enable_cpuacct_stat;
1166                 debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
1167             }
1168             else
1169                 debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1170         }
1171
1172         if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))) {
1173             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
1174             if(likely(stat(filename, &buf) != -1)) {
1175                 cg->cpuacct_usage.filename = strdupz(filename);
1176                 cg->cpuacct_usage.enabled = cgroup_enable_cpuacct_usage;
1177                 debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
1178             }
1179             else
1180                 debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1181         }
1182
1183         if(unlikely((cgroup_enable_detailed_memory || cgroup_used_memory_without_cache) && !cg->memory.filename_detailed && (cgroup_used_memory_without_cache || cgroup_enable_systemd_services_detailed_memory || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))) {
1184             snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
1185             if(likely(stat(filename, &buf) != -1)) {
1186                 cg->memory.filename_detailed = strdupz(filename);
1187                 cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_BOOLEAN_YES)?CONFIG_BOOLEAN_YES:CONFIG_BOOLEAN_AUTO;
1188                 debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_detailed);
1189             }
1190             else
1191                 debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1192         }
1193
1194         if(unlikely(cgroup_enable_memory && !cg->memory.filename_usage_in_bytes)) {
1195             snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
1196             if(likely(stat(filename, &buf) != -1)) {
1197                 cg->memory.filename_usage_in_bytes = strdupz(filename);
1198                 cg->memory.enabled_usage_in_bytes = cgroup_enable_memory;
1199                 debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
1200             }
1201             else
1202                 debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1203         }
1204
1205         if(unlikely(cgroup_enable_swap && !cg->memory.filename_msw_usage_in_bytes)) {
1206             snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
1207             if(likely(stat(filename, &buf) != -1)) {
1208                 cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
1209                 cg->memory.enabled_msw_usage_in_bytes = cgroup_enable_swap;
1210                 debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
1211             }
1212             else
1213                 debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1214         }
1215
1216         if(unlikely(cgroup_enable_memory_failcnt && !cg->memory.filename_failcnt)) {
1217             snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
1218             if(likely(stat(filename, &buf) != -1)) {
1219                 cg->memory.filename_failcnt = strdupz(filename);
1220                 cg->memory.enabled_failcnt = cgroup_enable_memory_failcnt;
1221                 debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
1222             }
1223             else
1224                 debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1225         }
1226
1227         if(unlikely(cgroup_enable_blkio_io && !cg->io_service_bytes.filename)) {
1228             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
1229             if(likely(stat(filename, &buf) != -1)) {
1230                 cg->io_service_bytes.filename = strdupz(filename);
1231                 cg->io_service_bytes.enabled = cgroup_enable_blkio_io;
1232                 debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
1233             }
1234             else
1235                 debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1236         }
1237
1238         if(unlikely(cgroup_enable_blkio_ops && !cg->io_serviced.filename)) {
1239             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
1240             if(likely(stat(filename, &buf) != -1)) {
1241                 cg->io_serviced.filename = strdupz(filename);
1242                 cg->io_serviced.enabled = cgroup_enable_blkio_ops;
1243                 debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
1244             }
1245             else
1246                 debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1247         }
1248
1249         if(unlikely(cgroup_enable_blkio_throttle_io && !cg->throttle_io_service_bytes.filename)) {
1250             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
1251             if(likely(stat(filename, &buf) != -1)) {
1252                 cg->throttle_io_service_bytes.filename = strdupz(filename);
1253                 cg->throttle_io_service_bytes.enabled = cgroup_enable_blkio_throttle_io;
1254                 debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
1255             }
1256             else
1257                 debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1258         }
1259
1260         if(unlikely(cgroup_enable_blkio_throttle_ops && !cg->throttle_io_serviced.filename)) {
1261             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
1262             if(likely(stat(filename, &buf) != -1)) {
1263                 cg->throttle_io_serviced.filename = strdupz(filename);
1264                 cg->throttle_io_serviced.enabled = cgroup_enable_blkio_throttle_ops;
1265                 debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
1266             }
1267             else
1268                 debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1269         }
1270
1271         if(unlikely(cgroup_enable_blkio_merged_ops && !cg->io_merged.filename)) {
1272             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
1273             if(likely(stat(filename, &buf) != -1)) {
1274                 cg->io_merged.filename = strdupz(filename);
1275                 cg->io_merged.enabled = cgroup_enable_blkio_merged_ops;
1276                 debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
1277             }
1278             else
1279                 debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1280         }
1281
1282         if(unlikely(cgroup_enable_blkio_queued_ops && !cg->io_queued.filename)) {
1283             snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
1284             if(likely(stat(filename, &buf) != -1)) {
1285                 cg->io_queued.filename = strdupz(filename);
1286                 cg->io_queued.enabled = cgroup_enable_blkio_queued_ops;
1287                 debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
1288             }
1289             else
1290                 debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1291         }
1292     }
1293
1294     debug(D_CGROUP, "done searching for cgroups");
1295     return;
1296 }
1297
1298 // ----------------------------------------------------------------------------
1299 // generate charts
1300
1301 #define CHART_TITLE_MAX 300
1302
1303 void update_systemd_services_charts(
1304           int update_every
1305         , int do_cpu
1306         , int do_mem_usage
1307         , int do_mem_detailed
1308         , int do_mem_failcnt
1309         , int do_swap_usage
1310         , int do_io
1311         , int do_io_ops
1312         , int do_throttle_io
1313         , int do_throttle_ops
1314         , int do_queued_ops
1315         , int do_merged_ops
1316 ) {
1317     static RRDSET
1318         *st_cpu = NULL,
1319         *st_mem_usage = NULL,
1320         *st_mem_failcnt = NULL,
1321         *st_swap_usage = NULL,
1322
1323         *st_mem_detailed_cache = NULL,
1324         *st_mem_detailed_rss = NULL,
1325         *st_mem_detailed_mapped = NULL,
1326         *st_mem_detailed_writeback = NULL,
1327         *st_mem_detailed_pgfault = NULL,
1328         *st_mem_detailed_pgmajfault = NULL,
1329         *st_mem_detailed_pgpgin = NULL,
1330         *st_mem_detailed_pgpgout = NULL,
1331
1332         *st_io_read = NULL,
1333         *st_io_serviced_read = NULL,
1334         *st_throttle_io_read = NULL,
1335         *st_throttle_ops_read = NULL,
1336         *st_queued_ops_read = NULL,
1337         *st_merged_ops_read = NULL,
1338
1339         *st_io_write = NULL,
1340         *st_io_serviced_write = NULL,
1341         *st_throttle_io_write = NULL,
1342         *st_throttle_ops_write = NULL,
1343         *st_queued_ops_write = NULL,
1344         *st_merged_ops_write = NULL;
1345
1346     // create the charts
1347
1348     if(likely(do_cpu)) {
1349         if(unlikely(!st_cpu)) {
1350             char title[CHART_TITLE_MAX + 1];
1351             snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : "");
1352
1353             st_cpu = rrdset_create_localhost(
1354                     "services"
1355                     , "cpu"
1356                     , NULL
1357                     , "cpu"
1358                     , "services.cpu"
1359                     , title
1360                     , "%"
1361                     , CHART_PRIORITY_SYSTEMD_SERVICES
1362                     , update_every
1363                     , RRDSET_TYPE_STACKED
1364             );
1365
1366         }
1367         else
1368             rrdset_next(st_cpu);
1369     }
1370
1371     if(likely(do_mem_usage)) {
1372         if(unlikely(!st_mem_usage)) {
1373
1374             st_mem_usage = rrdset_create_localhost(
1375                     "services"
1376                     , "mem_usage"
1377                     , NULL
1378                     , "mem"
1379                     , "services.mem_usage"
1380                     , (cgroup_used_memory_without_cache) ? "Systemd Services Used Memory without Cache"
1381                                                          : "Systemd Services Used Memory"
1382                     , "MB"
1383                     , CHART_PRIORITY_SYSTEMD_SERVICES + 10
1384                     , update_every
1385                     , RRDSET_TYPE_STACKED
1386             );
1387
1388         }
1389         else
1390             rrdset_next(st_mem_usage);
1391     }
1392
1393     if(likely(do_mem_detailed)) {
1394         if(unlikely(!st_mem_detailed_rss)) {
1395
1396             st_mem_detailed_rss = rrdset_create_localhost(
1397                     "services"
1398                     , "mem_rss"
1399                     , NULL
1400                     , "mem"
1401                     , "services.mem_rss"
1402                     , "Systemd Services RSS Memory"
1403                     , "MB"
1404                     , CHART_PRIORITY_SYSTEMD_SERVICES + 20
1405                     , update_every
1406                     , RRDSET_TYPE_STACKED
1407             );
1408
1409         }
1410         else
1411             rrdset_next(st_mem_detailed_rss);
1412
1413         if(unlikely(!st_mem_detailed_mapped)) {
1414
1415             st_mem_detailed_mapped = rrdset_create_localhost(
1416                     "services"
1417                     , "mem_mapped"
1418                     , NULL
1419                     , "mem"
1420                     , "services.mem_mapped"
1421                     , "Systemd Services Mapped Memory"
1422                     , "MB"
1423                     , CHART_PRIORITY_SYSTEMD_SERVICES + 30
1424                     , update_every
1425                     , RRDSET_TYPE_STACKED
1426             );
1427
1428         }
1429         else
1430             rrdset_next(st_mem_detailed_mapped);
1431
1432         if(unlikely(!st_mem_detailed_cache)) {
1433
1434             st_mem_detailed_cache = rrdset_create_localhost(
1435                     "services"
1436                     , "mem_cache"
1437                     , NULL
1438                     , "mem"
1439                     , "services.mem_cache"
1440                     , "Systemd Services Cache Memory"
1441                     , "MB"
1442                     , CHART_PRIORITY_SYSTEMD_SERVICES + 40
1443                     , update_every
1444                     , RRDSET_TYPE_STACKED
1445             );
1446
1447         }
1448         else
1449             rrdset_next(st_mem_detailed_cache);
1450
1451         if(unlikely(!st_mem_detailed_writeback)) {
1452
1453             st_mem_detailed_writeback = rrdset_create_localhost(
1454                     "services"
1455                     , "mem_writeback"
1456                     , NULL
1457                     , "mem"
1458                     , "services.mem_writeback"
1459                     , "Systemd Services Writeback Memory"
1460                     , "MB"
1461                     , CHART_PRIORITY_SYSTEMD_SERVICES + 50
1462                     , update_every
1463                     , RRDSET_TYPE_STACKED
1464             );
1465
1466         }
1467         else
1468             rrdset_next(st_mem_detailed_writeback);
1469
1470         if(unlikely(!st_mem_detailed_pgfault)) {
1471
1472             st_mem_detailed_pgfault = rrdset_create_localhost(
1473                     "services"
1474                     , "mem_pgfault"
1475                     , NULL
1476                     , "mem"
1477                     , "services.mem_pgfault"
1478                     , "Systemd Services Memory Minor Page Faults"
1479                     , "MB/s"
1480                     , CHART_PRIORITY_SYSTEMD_SERVICES + 60
1481                     , update_every
1482                     , RRDSET_TYPE_STACKED
1483             );
1484         }
1485         else
1486             rrdset_next(st_mem_detailed_pgfault);
1487
1488         if(unlikely(!st_mem_detailed_pgmajfault)) {
1489
1490             st_mem_detailed_pgmajfault = rrdset_create_localhost(
1491                     "services"
1492                     , "mem_pgmajfault"
1493                     , NULL
1494                     , "mem"
1495                     , "services.mem_pgmajfault"
1496                     , "Systemd Services Memory Major Page Faults"
1497                     , "MB/s"
1498                     , CHART_PRIORITY_SYSTEMD_SERVICES + 70
1499                     , update_every
1500                     , RRDSET_TYPE_STACKED
1501             );
1502
1503         }
1504         else
1505             rrdset_next(st_mem_detailed_pgmajfault);
1506
1507         if(unlikely(!st_mem_detailed_pgpgin)) {
1508
1509             st_mem_detailed_pgpgin = rrdset_create_localhost(
1510                     "services"
1511                     , "mem_pgpgin"
1512                     , NULL
1513                     , "mem"
1514                     , "services.mem_pgpgin"
1515                     , "Systemd Services Memory Charging Activity"
1516                     , "MB/s"
1517                     , CHART_PRIORITY_SYSTEMD_SERVICES + 80
1518                     , update_every
1519                     , RRDSET_TYPE_STACKED
1520             );
1521
1522         }
1523         else
1524             rrdset_next(st_mem_detailed_pgpgin);
1525
1526         if(unlikely(!st_mem_detailed_pgpgout)) {
1527
1528             st_mem_detailed_pgpgout = rrdset_create_localhost(
1529                     "services"
1530                     , "mem_pgpgout"
1531                     , NULL
1532                     , "mem"
1533                     , "services.mem_pgpgout"
1534                     , "Systemd Services Memory Uncharging Activity"
1535                     , "MB/s"
1536                     , CHART_PRIORITY_SYSTEMD_SERVICES + 90
1537                     , update_every
1538                     , RRDSET_TYPE_STACKED
1539             );
1540
1541         }
1542         else
1543             rrdset_next(st_mem_detailed_pgpgout);
1544     }
1545
1546     if(likely(do_mem_failcnt)) {
1547         if(unlikely(!st_mem_failcnt)) {
1548
1549             st_mem_failcnt = rrdset_create_localhost(
1550                     "services"
1551                     , "mem_failcnt"
1552                     , NULL
1553                     , "mem"
1554                     , "services.mem_failcnt"
1555                     , "Systemd Services Memory Limit Failures"
1556                     , "MB"
1557                     , CHART_PRIORITY_SYSTEMD_SERVICES + 110
1558                     , update_every
1559                     , RRDSET_TYPE_STACKED
1560             );
1561
1562         }
1563         else
1564             rrdset_next(st_mem_failcnt);
1565     }
1566
1567     if(likely(do_swap_usage)) {
1568         if(unlikely(!st_swap_usage)) {
1569
1570             st_swap_usage = rrdset_create_localhost(
1571                     "services"
1572                     , "swap_usage"
1573                     , NULL
1574                     , "swap"
1575                     , "services.swap_usage"
1576                     , "Systemd Services Swap Memory Used"
1577                     , "MB"
1578                     , CHART_PRIORITY_SYSTEMD_SERVICES + 100
1579                     , update_every
1580                     , RRDSET_TYPE_STACKED
1581             );
1582
1583         }
1584         else
1585             rrdset_next(st_swap_usage);
1586     }
1587
1588     if(likely(do_io)) {
1589         if(unlikely(!st_io_read)) {
1590
1591             st_io_read = rrdset_create_localhost(
1592                     "services"
1593                     , "io_read"
1594                     , NULL
1595                     , "disk"
1596                     , "services.io_read"
1597                     , "Systemd Services Disk Read Bandwidth"
1598                     , "KB/s"
1599                     , CHART_PRIORITY_SYSTEMD_SERVICES + 120
1600                     , update_every
1601                     , RRDSET_TYPE_STACKED
1602             );
1603
1604         }
1605         else
1606             rrdset_next(st_io_read);
1607
1608         if(unlikely(!st_io_write)) {
1609
1610             st_io_write = rrdset_create_localhost(
1611                     "services"
1612                     , "io_write"
1613                     , NULL
1614                     , "disk"
1615                     , "services.io_write"
1616                     , "Systemd Services Disk Write Bandwidth"
1617                     , "KB/s"
1618                     , CHART_PRIORITY_SYSTEMD_SERVICES + 130
1619                     , update_every
1620                     , RRDSET_TYPE_STACKED
1621             );
1622
1623         }
1624         else
1625             rrdset_next(st_io_write);
1626     }
1627
1628     if(likely(do_io_ops)) {
1629         if(unlikely(!st_io_serviced_read)) {
1630
1631             st_io_serviced_read = rrdset_create_localhost(
1632                     "services"
1633                     , "io_ops_read"
1634                     , NULL
1635                     , "disk"
1636                     , "services.io_ops_read"
1637                     , "Systemd Services Disk Read Operations"
1638                     , "operations/s"
1639                     , CHART_PRIORITY_SYSTEMD_SERVICES + 140
1640                     , update_every
1641                     , RRDSET_TYPE_STACKED
1642             );
1643
1644         }
1645         else
1646             rrdset_next(st_io_serviced_read);
1647
1648         if(unlikely(!st_io_serviced_write)) {
1649
1650             st_io_serviced_write = rrdset_create_localhost(
1651                     "services"
1652                     , "io_ops_write"
1653                     , NULL
1654                     , "disk"
1655                     , "services.io_ops_write"
1656                     , "Systemd Services Disk Write Operations"
1657                     , "operations/s"
1658                     , CHART_PRIORITY_SYSTEMD_SERVICES + 150
1659                     , update_every
1660                     , RRDSET_TYPE_STACKED
1661             );
1662
1663         }
1664         else
1665             rrdset_next(st_io_serviced_write);
1666     }
1667
1668     if(likely(do_throttle_io)) {
1669         if(unlikely(!st_throttle_io_read)) {
1670
1671             st_throttle_io_read = rrdset_create_localhost(
1672                     "services"
1673                     , "throttle_io_read"
1674                     , NULL
1675                     , "disk"
1676                     , "services.throttle_io_read"
1677                     , "Systemd Services Throttle Disk Read Bandwidth"
1678                     , "KB/s"
1679                     , CHART_PRIORITY_SYSTEMD_SERVICES + 160
1680                     , update_every
1681                     , RRDSET_TYPE_STACKED
1682             );
1683
1684         }
1685         else
1686             rrdset_next(st_throttle_io_read);
1687
1688         if(unlikely(!st_throttle_io_write)) {
1689
1690             st_throttle_io_write = rrdset_create_localhost(
1691                     "services"
1692                     , "throttle_io_write"
1693                     , NULL
1694                     , "disk"
1695                     , "services.throttle_io_write"
1696                     , "Systemd Services Throttle Disk Write Bandwidth"
1697                     , "KB/s"
1698                     , CHART_PRIORITY_SYSTEMD_SERVICES + 170
1699                     , update_every
1700                     , RRDSET_TYPE_STACKED
1701             );
1702
1703         }
1704         else
1705             rrdset_next(st_throttle_io_write);
1706     }
1707
1708     if(likely(do_throttle_ops)) {
1709         if(unlikely(!st_throttle_ops_read)) {
1710
1711             st_throttle_ops_read = rrdset_create_localhost(
1712                     "services"
1713                     , "throttle_io_ops_read"
1714                     , NULL
1715                     , "disk"
1716                     , "services.throttle_io_ops_read"
1717                     , "Systemd Services Throttle Disk Read Operations"
1718                     , "operations/s"
1719                     , CHART_PRIORITY_SYSTEMD_SERVICES + 180
1720                     , update_every
1721                     , RRDSET_TYPE_STACKED
1722             );
1723
1724         }
1725         else
1726             rrdset_next(st_throttle_ops_read);
1727
1728         if(unlikely(!st_throttle_ops_write)) {
1729
1730             st_throttle_ops_write = rrdset_create_localhost(
1731                     "services"
1732                     , "throttle_io_ops_write"
1733                     , NULL
1734                     , "disk"
1735                     , "services.throttle_io_ops_write"
1736                     , "Systemd Services Throttle Disk Write Operations"
1737                     , "operations/s"
1738                     , CHART_PRIORITY_SYSTEMD_SERVICES + 190
1739                     , update_every
1740                     , RRDSET_TYPE_STACKED
1741             );
1742
1743         }
1744         else
1745             rrdset_next(st_throttle_ops_write);
1746     }
1747
1748     if(likely(do_queued_ops)) {
1749         if(unlikely(!st_queued_ops_read)) {
1750
1751             st_queued_ops_read = rrdset_create_localhost(
1752                     "services"
1753                     , "queued_io_ops_read"
1754                     , NULL
1755                     , "disk"
1756                     , "services.queued_io_ops_read"
1757                     , "Systemd Services Queued Disk Read Operations"
1758                     , "operations/s"
1759                     , CHART_PRIORITY_SYSTEMD_SERVICES + 200
1760                     , update_every
1761                     , RRDSET_TYPE_STACKED
1762             );
1763
1764         }
1765         else
1766             rrdset_next(st_queued_ops_read);
1767
1768         if(unlikely(!st_queued_ops_write)) {
1769
1770             st_queued_ops_write = rrdset_create_localhost(
1771                     "services"
1772                     , "queued_io_ops_write"
1773                     , NULL
1774                     , "disk"
1775                     , "services.queued_io_ops_write"
1776                     , "Systemd Services Queued Disk Write Operations"
1777                     , "operations/s"
1778                     , CHART_PRIORITY_SYSTEMD_SERVICES + 210
1779                     , update_every
1780                     , RRDSET_TYPE_STACKED
1781             );
1782
1783         }
1784         else
1785             rrdset_next(st_queued_ops_write);
1786     }
1787
1788     if(likely(do_merged_ops)) {
1789         if(unlikely(!st_merged_ops_read)) {
1790
1791             st_merged_ops_read = rrdset_create_localhost(
1792                     "services"
1793                     , "merged_io_ops_read"
1794                     , NULL
1795                     , "disk"
1796                     , "services.merged_io_ops_read"
1797                     , "Systemd Services Merged Disk Read Operations"
1798                     , "operations/s"
1799                     , CHART_PRIORITY_SYSTEMD_SERVICES + 220
1800                     , update_every
1801                     , RRDSET_TYPE_STACKED
1802             );
1803
1804         }
1805         else
1806             rrdset_next(st_merged_ops_read);
1807
1808         if(unlikely(!st_merged_ops_write)) {
1809
1810             st_merged_ops_write = rrdset_create_localhost(
1811                     "services"
1812                     , "merged_io_ops_write"
1813                     , NULL
1814                     , "disk"
1815                     , "services.merged_io_ops_write"
1816                     , "Systemd Services Merged Disk Write Operations"
1817                     , "operations/s"
1818                     , CHART_PRIORITY_SYSTEMD_SERVICES + 230
1819                     , update_every
1820                     , RRDSET_TYPE_STACKED
1821             );
1822
1823         }
1824         else
1825             rrdset_next(st_merged_ops_write);
1826     }
1827
1828     // update the values
1829     struct cgroup *cg;
1830     for(cg = cgroup_root; cg ; cg = cg->next) {
1831         if(unlikely(!cg->available || !cg->enabled || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))
1832             continue;
1833
1834         if(likely(do_cpu && cg->cpuacct_stat.updated)) {
1835             if(unlikely(!cg->rd_cpu))
1836                 cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRD_ALGORITHM_INCREMENTAL);
1837
1838             rrddim_set_by_pointer(st_cpu, cg->rd_cpu, cg->cpuacct_stat.user + cg->cpuacct_stat.system);
1839         }
1840
1841         if(likely(do_mem_usage && cg->memory.updated_usage_in_bytes)) {
1842             if(unlikely(!cg->rd_mem_usage))
1843                 cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1844
1845             rrddim_set_by_pointer(st_mem_usage, cg->rd_mem_usage, cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0));
1846         }
1847
1848         if(likely(do_mem_detailed && cg->memory.updated_detailed)) {
1849             if(unlikely(!cg->rd_mem_detailed_rss))
1850                 cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1851
1852             rrddim_set_by_pointer(st_mem_detailed_rss, cg->rd_mem_detailed_rss, cg->memory.rss + cg->memory.rss_huge);
1853
1854             if(unlikely(!cg->rd_mem_detailed_mapped))
1855                 cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1856
1857             rrddim_set_by_pointer(st_mem_detailed_mapped, cg->rd_mem_detailed_mapped, cg->memory.mapped_file);
1858
1859             if(unlikely(!cg->rd_mem_detailed_cache))
1860                 cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1861
1862             rrddim_set_by_pointer(st_mem_detailed_cache, cg->rd_mem_detailed_cache, cg->memory.cache);
1863
1864             if(unlikely(!cg->rd_mem_detailed_writeback))
1865                 cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1866
1867             rrddim_set_by_pointer(st_mem_detailed_writeback, cg->rd_mem_detailed_writeback, cg->memory.writeback);
1868
1869             if(unlikely(!cg->rd_mem_detailed_pgfault))
1870                 cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
1871
1872             rrddim_set_by_pointer(st_mem_detailed_pgfault, cg->rd_mem_detailed_pgfault, cg->memory.pgfault);
1873
1874             if(unlikely(!cg->rd_mem_detailed_pgmajfault))
1875                 cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
1876
1877             rrddim_set_by_pointer(st_mem_detailed_pgmajfault, cg->rd_mem_detailed_pgmajfault, cg->memory.pgmajfault);
1878
1879             if(unlikely(!cg->rd_mem_detailed_pgpgin))
1880                 cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
1881
1882             rrddim_set_by_pointer(st_mem_detailed_pgpgin, cg->rd_mem_detailed_pgpgin, cg->memory.pgpgin);
1883
1884             if(unlikely(!cg->rd_mem_detailed_pgpgout))
1885                 cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
1886
1887             rrddim_set_by_pointer(st_mem_detailed_pgpgout, cg->rd_mem_detailed_pgpgout, cg->memory.pgpgout);
1888         }
1889
1890         if(likely(do_mem_failcnt && cg->memory.updated_failcnt)) {
1891             if(unlikely(!cg->rd_mem_failcnt))
1892                 cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1893
1894             rrddim_set_by_pointer(st_mem_failcnt, cg->rd_mem_failcnt, cg->memory.failcnt);
1895         }
1896
1897         if(likely(do_swap_usage && cg->memory.updated_msw_usage_in_bytes)) {
1898             if(unlikely(!cg->rd_swap_usage))
1899                 cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
1900
1901             rrddim_set_by_pointer(st_swap_usage, cg->rd_swap_usage, cg->memory.msw_usage_in_bytes);
1902         }
1903
1904         if(likely(do_io && cg->io_service_bytes.updated)) {
1905             if(unlikely(!cg->rd_io_service_bytes_read))
1906                 cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
1907
1908             rrddim_set_by_pointer(st_io_read, cg->rd_io_service_bytes_read, cg->io_service_bytes.Read);
1909
1910             if(unlikely(!cg->rd_io_service_bytes_write))
1911                 cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
1912
1913             rrddim_set_by_pointer(st_io_write, cg->rd_io_service_bytes_write, cg->io_service_bytes.Write);
1914         }
1915
1916         if(likely(do_io_ops && cg->io_serviced.updated)) {
1917             if(unlikely(!cg->rd_io_serviced_read))
1918                 cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1919
1920             rrddim_set_by_pointer(st_io_serviced_read, cg->rd_io_serviced_read, cg->io_serviced.Read);
1921
1922             if(unlikely(!cg->rd_io_serviced_write))
1923                 cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1924
1925             rrddim_set_by_pointer(st_io_serviced_write, cg->rd_io_serviced_write, cg->io_serviced.Write);
1926         }
1927
1928         if(likely(do_throttle_io && cg->throttle_io_service_bytes.updated)) {
1929             if(unlikely(!cg->rd_throttle_io_read))
1930                 cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
1931
1932             rrddim_set_by_pointer(st_throttle_io_read, cg->rd_throttle_io_read, cg->throttle_io_service_bytes.Read);
1933
1934             if(unlikely(!cg->rd_throttle_io_write))
1935                 cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
1936
1937             rrddim_set_by_pointer(st_throttle_io_write, cg->rd_throttle_io_write, cg->throttle_io_service_bytes.Write);
1938         }
1939
1940         if(likely(do_throttle_ops && cg->throttle_io_serviced.updated)) {
1941             if(unlikely(!cg->rd_throttle_io_serviced_read))
1942                 cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1943
1944             rrddim_set_by_pointer(st_throttle_ops_read, cg->rd_throttle_io_serviced_read, cg->throttle_io_serviced.Read);
1945
1946             if(unlikely(!cg->rd_throttle_io_serviced_write))
1947                 cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1948
1949             rrddim_set_by_pointer(st_throttle_ops_write, cg->rd_throttle_io_serviced_write, cg->throttle_io_serviced.Write);
1950         }
1951
1952         if(likely(do_queued_ops && cg->io_queued.updated)) {
1953             if(unlikely(!cg->rd_io_queued_read))
1954                 cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1955
1956             rrddim_set_by_pointer(st_queued_ops_read, cg->rd_io_queued_read, cg->io_queued.Read);
1957
1958             if(unlikely(!cg->rd_io_queued_write))
1959                 cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1960
1961             rrddim_set_by_pointer(st_queued_ops_write, cg->rd_io_queued_write, cg->io_queued.Write);
1962         }
1963
1964         if(likely(do_merged_ops && cg->io_merged.updated)) {
1965             if(unlikely(!cg->rd_io_merged_read))
1966                 cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1967
1968             rrddim_set_by_pointer(st_merged_ops_read, cg->rd_io_merged_read, cg->io_merged.Read);
1969
1970             if(unlikely(!cg->rd_io_merged_write))
1971                 cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
1972
1973             rrddim_set_by_pointer(st_merged_ops_write, cg->rd_io_merged_write, cg->io_merged.Write);
1974         }
1975     }
1976
1977     // complete the iteration
1978     if(likely(do_cpu))
1979         rrdset_done(st_cpu);
1980
1981     if(likely(do_mem_usage))
1982         rrdset_done(st_mem_usage);
1983
1984     if(unlikely(do_mem_detailed)) {
1985         rrdset_done(st_mem_detailed_cache);
1986         rrdset_done(st_mem_detailed_rss);
1987         rrdset_done(st_mem_detailed_mapped);
1988         rrdset_done(st_mem_detailed_writeback);
1989         rrdset_done(st_mem_detailed_pgfault);
1990         rrdset_done(st_mem_detailed_pgmajfault);
1991         rrdset_done(st_mem_detailed_pgpgin);
1992         rrdset_done(st_mem_detailed_pgpgout);
1993     }
1994
1995     if(likely(do_mem_failcnt))
1996         rrdset_done(st_mem_failcnt);
1997
1998     if(likely(do_swap_usage))
1999         rrdset_done(st_swap_usage);
2000
2001     if(likely(do_io)) {
2002         rrdset_done(st_io_read);
2003         rrdset_done(st_io_write);
2004     }
2005
2006     if(likely(do_io_ops)) {
2007         rrdset_done(st_io_serviced_read);
2008         rrdset_done(st_io_serviced_write);
2009     }
2010
2011     if(likely(do_throttle_io)) {
2012         rrdset_done(st_throttle_io_read);
2013         rrdset_done(st_throttle_io_write);
2014     }
2015
2016     if(likely(do_throttle_ops)) {
2017         rrdset_done(st_throttle_ops_read);
2018         rrdset_done(st_throttle_ops_write);
2019     }
2020
2021     if(likely(do_queued_ops)) {
2022         rrdset_done(st_queued_ops_read);
2023         rrdset_done(st_queued_ops_write);
2024     }
2025
2026     if(likely(do_merged_ops)) {
2027         rrdset_done(st_merged_ops_read);
2028         rrdset_done(st_merged_ops_write);
2029     }
2030 }
2031
2032 static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) {
2033     if(buffer[0]) return buffer;
2034
2035     if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0'))
2036         strncpy(buffer, "cgroup_root", len);
2037     else
2038         snprintfz(buffer, len, "cgroup_%s", id);
2039
2040     netdata_fix_chart_id(buffer);
2041     return buffer;
2042 }
2043
2044 void update_cgroup_charts(int update_every) {
2045     debug(D_CGROUP, "updating cgroups charts");
2046
2047     char type[RRD_ID_LENGTH_MAX + 1];
2048     char title[CHART_TITLE_MAX + 1];
2049
2050     int services_do_cpu = 0,
2051             services_do_mem_usage = 0,
2052             services_do_mem_detailed = 0,
2053             services_do_mem_failcnt = 0,
2054             services_do_swap_usage = 0,
2055             services_do_io = 0,
2056             services_do_io_ops = 0,
2057             services_do_throttle_io = 0,
2058             services_do_throttle_ops = 0,
2059             services_do_queued_ops = 0,
2060             services_do_merged_ops = 0;
2061
2062     struct cgroup *cg;
2063     for(cg = cgroup_root; cg ; cg = cg->next) {
2064         if(unlikely(!cg->available || !cg->enabled))
2065             continue;
2066
2067         if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) {
2068             if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES) services_do_cpu++;
2069
2070             if(cgroup_enable_systemd_services_detailed_memory && cg->memory.updated_detailed && cg->memory.enabled_detailed) services_do_mem_detailed++;
2071             if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_mem_usage++;
2072             if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES) services_do_mem_failcnt++;
2073             if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_swap_usage++;
2074
2075             if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_io++;
2076             if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_io_ops++;
2077             if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_io++;
2078             if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_ops++;
2079             if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES) services_do_queued_ops++;
2080             if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES) services_do_merged_ops++;
2081             continue;
2082         }
2083
2084         type[0] = '\0';
2085
2086         if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES)) {
2087             if(unlikely(!cg->st_cpu)) {
2088                 snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
2089
2090                 cg->st_cpu = rrdset_create_localhost(
2091                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2092                         , "cpu"
2093                         , NULL
2094                         , "cpu"
2095                         , "cgroup.cpu"
2096                         , title
2097                         , "%"
2098                         , CHART_PRIORITY_CONTAINERS
2099                         , update_every
2100                         , RRDSET_TYPE_STACKED
2101                 );
2102
2103                 rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL);
2104                 rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL);
2105             }
2106             else
2107                 rrdset_next(cg->st_cpu);
2108
2109             rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user);
2110             rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system);
2111             rrdset_done(cg->st_cpu);
2112         }
2113
2114         if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_BOOLEAN_YES)) {
2115             char id[RRD_ID_LENGTH_MAX + 1];
2116             unsigned int i;
2117
2118             if(unlikely(!cg->st_cpu_per_core)) {
2119                 snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
2120
2121                 cg->st_cpu_per_core = rrdset_create_localhost(
2122                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2123                         , "cpu_per_core"
2124                         , NULL
2125                         , "cpu"
2126                         , "cgroup.cpu_per_core"
2127                         , title
2128                         , "%"
2129                         , CHART_PRIORITY_CONTAINERS + 100
2130                         , update_every
2131                         , RRDSET_TYPE_STACKED
2132                 );
2133
2134                 for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
2135                     snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i);
2136                     rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRD_ALGORITHM_INCREMENTAL);
2137                 }
2138             }
2139             else
2140                 rrdset_next(cg->st_cpu_per_core);
2141
2142             for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
2143                 snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i);
2144                 rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]);
2145             }
2146             rrdset_done(cg->st_cpu_per_core);
2147         }
2148
2149         if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_BOOLEAN_YES)) {
2150             if(unlikely(!cg->st_mem)) {
2151                 snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
2152
2153                 cg->st_mem = rrdset_create_localhost(
2154                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2155                         , "mem"
2156                         , NULL
2157                         , "mem"
2158                         , "cgroup.mem"
2159                         , title
2160                         , "MB"
2161                         , CHART_PRIORITY_CONTAINERS + 210
2162                         , update_every
2163                         , RRDSET_TYPE_STACKED
2164                 );
2165
2166                 rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2167                 rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2168
2169                 if(cg->memory.detailed_has_swap)
2170                     rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2171
2172                 rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2173                 rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2174             }
2175             else
2176                 rrdset_next(cg->st_mem);
2177
2178             rrddim_set(cg->st_mem, "cache", cg->memory.cache);
2179             rrddim_set(cg->st_mem, "rss", cg->memory.rss);
2180
2181             if(cg->memory.detailed_has_swap)
2182                 rrddim_set(cg->st_mem, "swap", cg->memory.swap);
2183
2184             rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge);
2185             rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file);
2186             rrdset_done(cg->st_mem);
2187
2188             if(unlikely(!cg->st_writeback)) {
2189                 snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
2190
2191                 cg->st_writeback = rrdset_create_localhost(
2192                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2193                         , "writeback"
2194                         , NULL
2195                         , "mem"
2196                         , "cgroup.writeback"
2197                         , title
2198                         , "MB"
2199                         , CHART_PRIORITY_CONTAINERS + 300
2200                         , update_every
2201                         , RRDSET_TYPE_AREA
2202                 );
2203
2204                 if(cg->memory.detailed_has_dirty)
2205                     rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2206
2207                 rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2208             }
2209             else
2210                 rrdset_next(cg->st_writeback);
2211
2212             if(cg->memory.detailed_has_dirty)
2213                 rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty);
2214
2215             rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback);
2216             rrdset_done(cg->st_writeback);
2217
2218             if(unlikely(!cg->st_mem_activity)) {
2219                 snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
2220
2221                 cg->st_mem_activity = rrdset_create_localhost(
2222                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2223                         , "mem_activity"
2224                         , NULL
2225                         , "mem"
2226                         , "cgroup.mem_activity"
2227                         , title
2228                         , "MB/s"
2229                         , CHART_PRIORITY_CONTAINERS + 400
2230                         , update_every
2231                         , RRDSET_TYPE_LINE
2232                 );
2233
2234                 rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
2235                 rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
2236             }
2237             else
2238                 rrdset_next(cg->st_mem_activity);
2239
2240             rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.pgpgin);
2241             rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.pgpgout);
2242             rrdset_done(cg->st_mem_activity);
2243
2244             if(unlikely(!cg->st_pgfaults)) {
2245                 snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
2246
2247                 cg->st_pgfaults = rrdset_create_localhost(
2248                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2249                         , "pgfaults"
2250                         , NULL
2251                         , "mem"
2252                         , "cgroup.pgfaults"
2253                         , title
2254                         , "MB/s"
2255                         , CHART_PRIORITY_CONTAINERS + 500
2256                         , update_every
2257                         , RRDSET_TYPE_LINE
2258                 );
2259
2260                 rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
2261                 rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
2262             }
2263             else
2264                 rrdset_next(cg->st_pgfaults);
2265
2266             rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.pgfault);
2267             rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.pgmajfault);
2268             rrdset_done(cg->st_pgfaults);
2269         }
2270
2271         if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES)) {
2272             if(unlikely(!cg->st_mem_usage)) {
2273                 snprintfz(title, CHART_TITLE_MAX, "Used Memory %sfor cgroup %s", (cgroup_used_memory_without_cache && cg->memory.updated_detailed)?"without Cache ":"", cg->chart_title);
2274
2275                 cg->st_mem_usage = rrdset_create_localhost(
2276                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2277                         , "mem_usage"
2278                         , NULL
2279                         , "mem"
2280                         , "cgroup.mem_usage"
2281                         , title
2282                         , "MB"
2283                         , CHART_PRIORITY_CONTAINERS + 200
2284                         , update_every
2285                         , RRDSET_TYPE_STACKED
2286                 );
2287
2288                 rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2289                 rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
2290             }
2291             else
2292                 rrdset_next(cg->st_mem_usage);
2293
2294             rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0));
2295             rrddim_set(cg->st_mem_usage, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0);
2296             rrdset_done(cg->st_mem_usage);
2297         }
2298
2299         if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES)) {
2300             if(unlikely(!cg->st_mem_failcnt)) {
2301                 snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
2302
2303                 cg->st_mem_failcnt = rrdset_create_localhost(
2304                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2305                         , "mem_failcnt"
2306                         , NULL
2307                         , "mem"
2308                         , "cgroup.mem_failcnt"
2309                         , title
2310                         , "count"
2311                         , CHART_PRIORITY_CONTAINERS + 250
2312                         , update_every
2313                         , RRDSET_TYPE_LINE
2314                 );
2315
2316                 rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2317             }
2318             else
2319                 rrdset_next(cg->st_mem_failcnt);
2320
2321             rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt);
2322             rrdset_done(cg->st_mem_failcnt);
2323         }
2324
2325         if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) {
2326             if(unlikely(!cg->st_io)) {
2327                 snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
2328
2329                 cg->st_io = rrdset_create_localhost(
2330                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2331                         , "io"
2332                         , NULL
2333                         , "disk"
2334                         , "cgroup.io"
2335                         , title
2336                         , "KB/s"
2337                         , CHART_PRIORITY_CONTAINERS + 1200
2338                         , update_every
2339                         , RRDSET_TYPE_AREA
2340                 );
2341
2342                 rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
2343                 rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
2344             }
2345             else
2346                 rrdset_next(cg->st_io);
2347
2348             rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read);
2349             rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write);
2350             rrdset_done(cg->st_io);
2351         }
2352
2353         if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES)) {
2354             if(unlikely(!cg->st_serviced_ops)) {
2355                 snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
2356
2357                 cg->st_serviced_ops = rrdset_create_localhost(
2358                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2359                         , "serviced_ops"
2360                         , NULL
2361                         , "disk"
2362                         , "cgroup.serviced_ops"
2363                         , title
2364                         , "operations/s"
2365                         , CHART_PRIORITY_CONTAINERS + 1200
2366                         , update_every
2367                         , RRDSET_TYPE_LINE
2368                 );
2369
2370                 rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2371                 rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2372             }
2373             else
2374                 rrdset_next(cg->st_serviced_ops);
2375
2376             rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read);
2377             rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write);
2378             rrdset_done(cg->st_serviced_ops);
2379         }
2380
2381         if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) {
2382             if(unlikely(!cg->st_throttle_io)) {
2383                 snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
2384
2385                 cg->st_throttle_io = rrdset_create_localhost(
2386                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2387                         , "throttle_io"
2388                         , NULL
2389                         , "disk"
2390                         , "cgroup.throttle_io"
2391                         , title
2392                         , "KB/s"
2393                         , CHART_PRIORITY_CONTAINERS + 1200
2394                         , update_every
2395                         , RRDSET_TYPE_AREA
2396                 );
2397
2398                 rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
2399                 rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
2400             }
2401             else
2402                 rrdset_next(cg->st_throttle_io);
2403
2404             rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read);
2405             rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write);
2406             rrdset_done(cg->st_throttle_io);
2407         }
2408
2409         if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES)) {
2410             if(unlikely(!cg->st_throttle_serviced_ops)) {
2411                 snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
2412
2413                 cg->st_throttle_serviced_ops = rrdset_create_localhost(
2414                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2415                         , "throttle_serviced_ops"
2416                         , NULL
2417                         , "disk"
2418                         , "cgroup.throttle_serviced_ops"
2419                         , title
2420                         , "operations/s"
2421                         , CHART_PRIORITY_CONTAINERS + 1200
2422                         , update_every
2423                         , RRDSET_TYPE_LINE
2424                 );
2425
2426                 rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2427                 rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2428             }
2429             else
2430                 rrdset_next(cg->st_throttle_serviced_ops);
2431
2432             rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read);
2433             rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write);
2434             rrdset_done(cg->st_throttle_serviced_ops);
2435         }
2436
2437         if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES)) {
2438             if(unlikely(!cg->st_queued_ops)) {
2439                 snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
2440
2441                 cg->st_queued_ops = rrdset_create_localhost(
2442                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2443                         , "queued_ops"
2444                         , NULL
2445                         , "disk"
2446                         , "cgroup.queued_ops"
2447                         , title
2448                         , "operations"
2449                         , CHART_PRIORITY_CONTAINERS + 2000
2450                         , update_every
2451                         , RRDSET_TYPE_LINE
2452                 );
2453
2454                 rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2455                 rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
2456             }
2457             else
2458                 rrdset_next(cg->st_queued_ops);
2459
2460             rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read);
2461             rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write);
2462             rrdset_done(cg->st_queued_ops);
2463         }
2464
2465         if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES)) {
2466             if(unlikely(!cg->st_merged_ops)) {
2467                 snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
2468
2469                 cg->st_merged_ops = rrdset_create_localhost(
2470                         cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
2471                         , "merged_ops"
2472                         , NULL
2473                         , "disk"
2474                         , "cgroup.merged_ops"
2475                         , title
2476                         , "operations/s"
2477                         , CHART_PRIORITY_CONTAINERS + 2100
2478                         , update_every
2479                         , RRDSET_TYPE_LINE
2480                 );
2481
2482                 rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
2483                 rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
2484             }
2485             else
2486                 rrdset_next(cg->st_merged_ops);
2487
2488             rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read);
2489             rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write);
2490             rrdset_done(cg->st_merged_ops);
2491         }
2492     }
2493
2494     if(likely(cgroup_enable_systemd_services))
2495         update_systemd_services_charts(update_every, services_do_cpu, services_do_mem_usage, services_do_mem_detailed
2496                                        , services_do_mem_failcnt, services_do_swap_usage, services_do_io
2497                                        , services_do_io_ops, services_do_throttle_io, services_do_throttle_ops
2498                                        , services_do_queued_ops, services_do_merged_ops
2499         );
2500
2501     debug(D_CGROUP, "done updating cgroups charts");
2502 }
2503
2504 // ----------------------------------------------------------------------------
2505 // cgroups main
2506
2507 void *cgroups_main(void *ptr) {
2508     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
2509
2510     info("CGROUP Plugin thread created with task id %d", gettid());
2511
2512     if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
2513         error("Cannot set pthread cancel type to DEFERRED.");
2514
2515     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
2516         error("Cannot set pthread cancel state to ENABLE.");
2517
2518     struct rusage thread;
2519
2520     // when ZERO, attempt to do it
2521     int vdo_cpu_netdata = config_get_boolean("plugin:cgroups", "cgroups plugin resource charts", 1);
2522
2523     read_cgroup_plugin_configuration();
2524
2525     RRDSET *stcpu_thread = NULL;
2526
2527     heartbeat_t hb;
2528     heartbeat_init(&hb);
2529     usec_t step = cgroup_update_every * USEC_PER_SEC;
2530     usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_dt = 0;
2531     for(;;) {
2532         usec_t hb_dt = heartbeat_next(&hb, step);
2533         if(unlikely(netdata_exit)) break;
2534
2535         // BEGIN -- the job to be done
2536
2537         find_dt += hb_dt;
2538         if(unlikely(find_dt >= find_every || cgroups_check)) {
2539             find_all_cgroups();
2540             find_dt = 0;
2541             cgroups_check = 0;
2542         }
2543
2544         read_all_cgroups(cgroup_root);
2545         update_cgroup_charts(cgroup_update_every);
2546
2547         // END -- the job is done
2548
2549         // --------------------------------------------------------------------
2550
2551         if(vdo_cpu_netdata) {
2552             getrusage(RUSAGE_THREAD, &thread);
2553
2554             if(unlikely(!stcpu_thread)) {
2555
2556                 stcpu_thread = rrdset_create_localhost(
2557                         "netdata"
2558                         , "plugin_cgroups_cpu"
2559                         , NULL
2560                         , "cgroups"
2561                         , NULL
2562                         , "NetData CGroups Plugin CPU usage"
2563                         , "milliseconds/s"
2564                         , 132000
2565                         , cgroup_update_every
2566                         , RRDSET_TYPE_STACKED
2567                 );
2568
2569                 rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRD_ALGORITHM_INCREMENTAL);
2570                 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
2571             }
2572             else
2573                 rrdset_next(stcpu_thread);
2574
2575             rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
2576             rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
2577             rrdset_done(stcpu_thread);
2578         }
2579     }
2580
2581     info("CGROUP thread exiting");
2582
2583     static_thread->enabled = 0;
2584     pthread_exit(NULL);
2585     return NULL;
2586 }