]> arthur.barton.de Git - netdata.git/blob - src/sys_fs_cgroup.c
024941eaac64b821d5891ac148fe0e5665142455
[netdata.git] / src / sys_fs_cgroup.c
1 #include "common.h"
2
3 // ----------------------------------------------------------------------------
4 // cgroup globals
5
6 static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
7 static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
8 static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
9 static int cgroup_enable_devices = CONFIG_ONDEMAND_ONDEMAND;
10 static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
11 static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_YES;
12 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
13 static int cgroup_check_for_new_every = 10;
14 static int cgroup_update_every = 1;
15 static int cgroup_recheck_zero_blkio_every_iterations = 10;
16 static int cgroup_recheck_zero_mem_failcnt_every_iterations = 10;
17
18 static char *cgroup_cpuacct_base = NULL;
19 static char *cgroup_blkio_base = NULL;
20 static char *cgroup_memory_base = NULL;
21 static char *cgroup_devices_base = NULL;
22
23 static int cgroup_root_count = 0;
24 static int cgroup_root_max = 500;
25 static int cgroup_max_depth = 0;
26
27 static SIMPLE_PATTERN *disabled_cgroups_patterns = NULL;
28 static SIMPLE_PATTERN *disabled_cgroup_paths = NULL;
29 static SIMPLE_PATTERN *disabled_cgroup_renames = NULL;
30 static SIMPLE_PATTERN *systemd_services_cgroups = NULL;
31
32 static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh";
33
34 static uint32_t Read_hash = 0;
35 static uint32_t Write_hash = 0;
36 static uint32_t Sync_hash = 0;
37 static uint32_t Async_hash = 0;
38 static uint32_t Total_hash = 0;
39 static uint32_t user_hash = 0;
40 static uint32_t system_hash = 0;
41 static uint32_t cache_hash = 0;
42 static uint32_t rss_hash = 0;
43 static uint32_t rss_huge_hash = 0;
44 static uint32_t mapped_file_hash = 0;
45 static uint32_t writeback_hash = 0;
46 static uint32_t dirty_hash = 0;
47 static uint32_t swap_hash = 0;
48 static uint32_t pgpgin_hash = 0;
49 static uint32_t pgpgout_hash = 0;
50 static uint32_t pgfault_hash = 0;
51 static uint32_t pgmajfault_hash = 0;
52 static uint32_t inactive_anon_hash = 0;
53 static uint32_t active_anon_hash = 0;
54 static uint32_t inactive_file_hash = 0;
55 static uint32_t active_file_hash = 0;
56 static uint32_t unevictable_hash = 0;
57 static uint32_t hierarchical_memory_limit_hash = 0;
58 static uint32_t total_cache_hash = 0;
59 static uint32_t total_rss_hash = 0;
60 static uint32_t total_rss_huge_hash = 0;
61 static uint32_t total_mapped_file_hash = 0;
62 static uint32_t total_writeback_hash = 0;
63 static uint32_t total_dirty_hash = 0;
64 static uint32_t total_swap_hash = 0;
65 static uint32_t total_pgpgin_hash = 0;
66 static uint32_t total_pgpgout_hash = 0;
67 static uint32_t total_pgfault_hash = 0;
68 static uint32_t total_pgmajfault_hash = 0;
69 static uint32_t total_inactive_anon_hash = 0;
70 static uint32_t total_active_anon_hash = 0;
71 static uint32_t total_inactive_file_hash = 0;
72 static uint32_t total_active_file_hash = 0;
73 static uint32_t total_unevictable_hash = 0;
74
75 void read_cgroup_plugin_configuration() {
76     Read_hash = simple_hash("Read");
77     Write_hash = simple_hash("Write");
78     Sync_hash = simple_hash("Sync");
79     Async_hash = simple_hash("Async");
80     Total_hash = simple_hash("Total");
81     user_hash = simple_hash("user");
82     system_hash = simple_hash("system");
83     cache_hash = simple_hash("cache");
84     rss_hash = simple_hash("rss");
85     rss_huge_hash = simple_hash("rss_huge");
86     mapped_file_hash = simple_hash("mapped_file");
87     writeback_hash = simple_hash("writeback");
88     dirty_hash = simple_hash("dirty");
89     swap_hash = simple_hash("swap");
90     pgpgin_hash = simple_hash("pgpgin");
91     pgpgout_hash = simple_hash("pgpgout");
92     pgfault_hash = simple_hash("pgfault");
93     pgmajfault_hash = simple_hash("pgmajfault");
94     inactive_anon_hash = simple_hash("inactive_anon");
95     active_anon_hash = simple_hash("active_anon");
96     inactive_file_hash = simple_hash("inactive_file");
97     active_file_hash = simple_hash("active_file");
98     unevictable_hash = simple_hash("unevictable");
99     hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
100     total_cache_hash = simple_hash("total_cache");
101     total_rss_hash = simple_hash("total_rss");
102     total_rss_huge_hash = simple_hash("total_rss_huge");
103     total_mapped_file_hash = simple_hash("total_mapped_file");
104     total_writeback_hash = simple_hash("total_writeback");
105     total_dirty_hash = simple_hash("total_dirty");
106     total_swap_hash = simple_hash("total_swap");
107     total_pgpgin_hash = simple_hash("total_pgpgin");
108     total_pgpgout_hash = simple_hash("total_pgpgout");
109     total_pgfault_hash = simple_hash("total_pgfault");
110     total_pgmajfault_hash = simple_hash("total_pgmajfault");
111     total_inactive_anon_hash = simple_hash("total_inactive_anon");
112     total_active_anon_hash = simple_hash("total_active_anon");
113     total_inactive_file_hash = simple_hash("total_inactive_file");
114     total_active_file_hash = simple_hash("total_active_file");
115     total_unevictable_hash = simple_hash("total_unevictable");
116
117     cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every);
118     if(cgroup_update_every < rrd_update_every)
119         cgroup_update_every = rrd_update_every;
120
121     cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every);
122     if(cgroup_check_for_new_every < cgroup_update_every)
123         cgroup_check_for_new_every = cgroup_update_every;
124
125     cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
126     cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
127     cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
128     cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
129
130     cgroup_enable_systemd_services = config_get_boolean_ondemand("plugin:cgroups", "enable systemd services", cgroup_enable_systemd_services);
131
132     cgroup_recheck_zero_blkio_every_iterations = (int)config_get_number("plugin:cgroups", "recheck zero blkio every iterations", cgroup_recheck_zero_blkio_every_iterations);
133     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);
134
135     char filename[FILENAME_MAX + 1], *s;
136     struct mountinfo *mi, *root = mountinfo_read(0);
137
138     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
139     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
140     if(!mi) {
141         error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
142         s = "/sys/fs/cgroup/cpuacct";
143     }
144     else s = mi->mount_point;
145     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
146     cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
147
148     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
149     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
150     if(!mi) {
151         error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
152         s = "/sys/fs/cgroup/blkio";
153     }
154     else s = mi->mount_point;
155     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
156     cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
157
158     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
159     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
160     if(!mi) {
161         error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
162         s = "/sys/fs/cgroup/memory";
163     }
164     else s = mi->mount_point;
165     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
166     cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
167
168     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
169     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
170     if(!mi) {
171         error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
172         s = "/sys/fs/cgroup/devices";
173     }
174     else s = mi->mount_point;
175     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
176     cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
177
178     cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
179     cgroup_max_depth = (int)config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
180
181     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);
182
183     disabled_cgroups_patterns = simple_pattern_create(
184             config_get("plugin:cgroups", "disable by default cgroups matching", " *.mount "
185                     " *.partition "
186                     " *.service "
187                     " *.slice "
188                     " *.swap "
189                     " *.user "
190                     " / "
191                     " /docker "
192                     " /init.scope "
193                     " /libvirt "
194                     " /lxc "
195                     " /lxc/*/ns "                          //  #1397
196                     " /machine "
197                     " /machine.slice "
198                     " /qemu "
199                     " /system "
200                     " /system.slice "
201                     " /systemd "
202                     " /user "
203                     " /user.slice "
204             ), SIMPLE_PATTERN_EXACT);
205
206     disabled_cgroup_paths = simple_pattern_create(
207             config_get("plugin:cgroups", "do not search for cgroups in paths matching"
208                        , " *-qemu "                             //  #345
209                             " /init.scope "
210                             " /system "
211                             " /systemd "
212                             " /user "
213                             " /user.slice "
214             ), SIMPLE_PATTERN_EXACT);
215
216     cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", cgroups_rename_script);
217
218     disabled_cgroup_renames = simple_pattern_create(
219             config_get("plugin:cgroups", "do not run script to rename cgroups matching", " / "
220                     " *.mount "
221                     " *.partition "
222                     " *.scope "
223                     " *.service "
224                     " *.slice "
225                     " *.swap "
226                     " *.user "
227             ), SIMPLE_PATTERN_EXACT);
228
229     if(cgroup_enable_systemd_services)
230         systemd_services_cgroups = simple_pattern_create(
231                 config_get("plugin:cgroups", "cgroups to match as systemd services"
232                            , " !/system.slice/*/*.service /system.slice/*.service "
233                 ), SIMPLE_PATTERN_EXACT);
234
235     mountinfo_free(root);
236 }
237
238 // ----------------------------------------------------------------------------
239 // cgroup objects
240
241 struct blkio {
242     int updated;
243     int delay_counter;
244
245     char *filename;
246
247     unsigned long long Read;
248     unsigned long long Write;
249 /*
250     unsigned long long Sync;
251     unsigned long long Async;
252     unsigned long long Total;
253 */
254 };
255
256 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
257 struct memory {
258     int updated;
259
260     char *filename;
261
262     int has_dirty_swap;
263
264     unsigned long long cache;
265     unsigned long long rss;
266     unsigned long long rss_huge;
267     unsigned long long mapped_file;
268     unsigned long long writeback;
269     unsigned long long dirty;
270     unsigned long long swap;
271     unsigned long long pgpgin;
272     unsigned long long pgpgout;
273     unsigned long long pgfault;
274     unsigned long long pgmajfault;
275 /*
276     unsigned long long inactive_anon;
277     unsigned long long active_anon;
278     unsigned long long inactive_file;
279     unsigned long long active_file;
280     unsigned long long unevictable;
281     unsigned long long hierarchical_memory_limit;
282     unsigned long long total_cache;
283     unsigned long long total_rss;
284     unsigned long long total_rss_huge;
285     unsigned long long total_mapped_file;
286     unsigned long long total_writeback;
287     unsigned long long total_dirty;
288     unsigned long long total_swap;
289     unsigned long long total_pgpgin;
290     unsigned long long total_pgpgout;
291     unsigned long long total_pgfault;
292     unsigned long long total_pgmajfault;
293     unsigned long long total_inactive_anon;
294     unsigned long long total_active_anon;
295     unsigned long long total_inactive_file;
296     unsigned long long total_active_file;
297     unsigned long long total_unevictable;
298 */
299
300     int usage_in_bytes_updated;
301     char *filename_usage_in_bytes;
302     unsigned long long usage_in_bytes;
303
304     int msw_usage_in_bytes_updated;
305     char *filename_msw_usage_in_bytes;
306     unsigned long long msw_usage_in_bytes;
307
308     int failcnt_updated;
309     int failcnt_delay_counter;
310     char *filename_failcnt;
311     unsigned long long failcnt;
312 };
313
314 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
315 struct cpuacct_stat {
316     int updated;
317
318     char *filename;
319
320     unsigned long long user;
321     unsigned long long system;
322 };
323
324 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
325 struct cpuacct_usage {
326     int updated;
327
328     char *filename;
329
330     unsigned int cpus;
331     unsigned long long *cpu_percpu;
332 };
333
334 #define CGROUP_OPTIONS_DISABLED_DUPLICATE   0x00000001
335 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
336
337 struct cgroup {
338     uint32_t options;
339
340     char available;      // found in the filesystem
341     char enabled;        // enabled in the config
342
343     char *id;
344     uint32_t hash;
345
346     char *chart_id;
347     uint32_t hash_chart;
348
349     char *chart_title;
350
351     struct cpuacct_stat cpuacct_stat;
352     struct cpuacct_usage cpuacct_usage;
353
354     struct memory memory;
355
356     struct blkio io_service_bytes;              // bytes
357     struct blkio io_serviced;                   // operations
358
359     struct blkio throttle_io_service_bytes;     // bytes
360     struct blkio throttle_io_serviced;          // operations
361
362     struct blkio io_merged;                     // operations
363     struct blkio io_queued;                     // operations
364
365     // per cgroup charts
366     RRDSET *st_cpu;
367     RRDSET *st_cpu_per_core;
368     RRDSET *st_mem;
369     RRDSET *st_writeback;
370     RRDSET *st_mem_activity;
371     RRDSET *st_pgfaults;
372     RRDSET *st_mem_usage;
373     RRDSET *st_mem_failcnt;
374     RRDSET *st_io;
375     RRDSET *st_serviced_ops;
376     RRDSET *st_throttle_io;
377     RRDSET *st_throttle_serviced_ops;
378     RRDSET *st_queued_ops;
379     RRDSET *st_merged_ops;
380
381     // services
382     RRDDIM *rd_cpu;
383     RRDDIM *rd_mem_usage;
384     RRDDIM *rd_mem_failcnt;
385     RRDDIM *rd_swap_usage;
386
387     RRDDIM *rd_io_service_bytes_read;
388     RRDDIM *rd_io_serviced_read;
389     RRDDIM *rd_throttle_io_read;
390     RRDDIM *rd_throttle_io_serviced_read;
391     RRDDIM *rd_io_queued_read;
392     RRDDIM *rd_io_merged_read;
393
394     RRDDIM *rd_io_service_bytes_write;
395     RRDDIM *rd_io_serviced_write;
396     RRDDIM *rd_throttle_io_write;
397     RRDDIM *rd_throttle_io_serviced_write;
398     RRDDIM *rd_io_queued_write;
399     RRDDIM *rd_io_merged_write;
400
401     struct cgroup *next;
402
403 } *cgroup_root = NULL;
404
405 // ----------------------------------------------------------------------------
406 // read values from /sys
407
408 static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
409     static procfile *ff = NULL;
410
411     if(likely(cp->filename)) {
412         ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
413         if(unlikely(!ff)) {
414             cp->updated = 0;
415             return;
416         }
417
418         ff = procfile_readall(ff);
419         if(unlikely(!ff)) {
420             cp->updated = 0;
421             return;
422         }
423
424         unsigned long i, lines = procfile_lines(ff);
425
426         if(unlikely(lines < 1)) {
427             error("File '%s' should have 1+ lines.", cp->filename);
428             cp->updated = 0;
429             return;
430         }
431
432         for(i = 0; i < lines ; i++) {
433             char *s = procfile_lineword(ff, i, 0);
434             uint32_t hash = simple_hash(s);
435
436             if(unlikely(hash == user_hash && !strcmp(s, "user"))) {
437                 cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
438                 continue;
439             }
440
441             if(unlikely(hash == system_hash && !strcmp(s, "system"))) {
442                 cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
443                 continue;
444             }
445         }
446
447         cp->updated = 1;
448     }
449 }
450
451 static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
452     static procfile *ff = NULL;
453
454     if(likely(ca->filename)) {
455         ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
456         if(unlikely(!ff)) {
457             ca->updated = 0;
458             return;
459         }
460
461         ff = procfile_readall(ff);
462         if(unlikely(!ff)) {
463             ca->updated = 0;
464             return;
465         }
466
467         if(unlikely(procfile_lines(ff) < 1)) {
468             error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
469             ca->updated = 0;
470             return;
471         }
472
473         unsigned long i = procfile_linewords(ff, 0);
474         if(unlikely(i == 0)) {
475             return;
476             ca->updated = 0;
477         }
478
479         // we may have 1 more CPU reported
480         while(i > 0) {
481             char *s = procfile_lineword(ff, 0, i - 1);
482             if(!*s) i--;
483             else break;
484         }
485
486         if(unlikely(i != ca->cpus)) {
487             freez(ca->cpu_percpu);
488             ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
489             ca->cpus = (unsigned int)i;
490         }
491
492         for(i = 0; i < ca->cpus ;i++)
493             ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
494
495         ca->updated = 1;
496     }
497 }
498
499 static inline void cgroup_read_blkio(struct blkio *io) {
500     static procfile *ff = NULL;
501
502     if(unlikely(io->delay_counter > 0)) {
503         io->delay_counter--;
504         return;
505     }
506
507     if(likely(io->filename)) {
508         ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
509         if(unlikely(!ff)) {
510             io->updated = 0;
511             return;
512         }
513
514         ff = procfile_readall(ff);
515         if(unlikely(!ff)) {
516             io->updated = 0;
517             return;
518         }
519
520         unsigned long i, lines = procfile_lines(ff);
521
522         if(unlikely(lines < 1)) {
523             error("File '%s' should have 1+ lines.", io->filename);
524             io->updated = 0;
525             return;
526         }
527
528         io->Read = 0;
529         io->Write = 0;
530 /*
531         io->Sync = 0;
532         io->Async = 0;
533         io->Total = 0;
534 */
535
536         for(i = 0; i < lines ; i++) {
537             char *s = procfile_lineword(ff, i, 1);
538             uint32_t hash = simple_hash(s);
539
540             if(unlikely(hash == Read_hash && !strcmp(s, "Read"))) {
541                 io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
542                 continue;
543             }
544
545             if(unlikely(hash == Write_hash && !strcmp(s, "Write"))) {
546                 io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
547                 continue;
548             }
549
550 /*
551             if(hash == Sync_hash && !strcmp(s, "Sync")) {
552                 io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
553                 continue;
554             }
555
556             if(hash == Async_hash && !strcmp(s, "Async")) {
557                 io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
558                 continue;
559             }
560
561             if(hash == Total_hash && !strcmp(s, "Total")) {
562                 io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
563                 continue;
564             }
565 */
566         }
567
568         io->updated = 1;
569
570         if(unlikely(!io->Read && !io->Write))
571             io->delay_counter = cgroup_recheck_zero_blkio_every_iterations;
572     }
573 }
574
575 static inline void cgroup_read_memory(struct memory *mem) {
576     static procfile *ff = NULL;
577
578     if(likely(mem->filename)) {
579         ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
580         if(unlikely(!ff)) {
581             mem->updated = 0;
582             goto memory_next;
583         }
584
585         ff = procfile_readall(ff);
586         if(unlikely(!ff)) {
587             mem->updated = 0;
588             goto memory_next;
589         }
590
591         unsigned long i, lines = procfile_lines(ff);
592
593         if(unlikely(lines < 1)) {
594             error("File '%s' should have 1+ lines.", mem->filename);
595             mem->updated = 0;
596             goto memory_next;
597         }
598
599         for(i = 0; i < lines ; i++) {
600             char *s = procfile_lineword(ff, i, 0);
601             uint32_t hash = simple_hash(s);
602
603             if(unlikely(hash == cache_hash && !strcmp(s, "cache"))) {
604                 mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
605                 continue;
606             }
607
608             if(unlikely(hash == rss_hash && !strcmp(s, "rss"))) {
609                 mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
610                 continue;
611             }
612
613             if(unlikely(hash == rss_huge_hash && !strcmp(s, "rss_huge"))) {
614                 mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
615                 continue;
616             }
617
618             if(unlikely(hash == mapped_file_hash && !strcmp(s, "mapped_file"))) {
619                 mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
620                 continue;
621             }
622
623             if(unlikely(hash == writeback_hash && !strcmp(s, "writeback"))) {
624                 mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
625                 continue;
626             }
627
628             if(unlikely(hash == dirty_hash && !strcmp(s, "dirty"))) {
629                 mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
630                 mem->has_dirty_swap = 1;
631                 continue;
632             }
633
634             if(unlikely(hash == swap_hash && !strcmp(s, "swap"))) {
635                 mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
636                 mem->has_dirty_swap = 1;
637                 continue;
638             }
639
640             if(unlikely(hash == pgpgin_hash && !strcmp(s, "pgpgin"))) {
641                 mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
642                 continue;
643             }
644
645             if(unlikely(hash == pgpgout_hash && !strcmp(s, "pgpgout"))) {
646                 mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
647                 continue;
648             }
649
650             if(unlikely(hash == pgfault_hash && !strcmp(s, "pgfault"))) {
651                 mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
652                 continue;
653             }
654
655             if(unlikely(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))) {
656                 mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
657                 continue;
658             }
659
660 /*
661             if(unlikely(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))) {
662                 mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
663                 continue;
664             }
665
666             if(unlikely(hash == active_anon_hash && !strcmp(s, "active_anon"))) {
667                 mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
668                 continue;
669             }
670
671             if(unlikely(hash == inactive_file_hash && !strcmp(s, "inactive_file"))) {
672                 mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
673                 continue;
674             }
675
676             if(unlikely(hash == active_file_hash && !strcmp(s, "active_file"))) {
677                 mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
678                 continue;
679             }
680
681             if(unlikely(hash == unevictable_hash && !strcmp(s, "unevictable"))) {
682                 mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
683                 continue;
684             }
685
686             if(unlikely(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))) {
687                 mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
688                 continue;
689             }
690
691             if(unlikely(hash == total_cache_hash && !strcmp(s, "total_cache"))) {
692                 mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
693                 continue;
694             }
695
696             if(unlikely(hash == total_rss_hash && !strcmp(s, "total_rss"))) {
697                 mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
698                 continue;
699             }
700
701             if(unlikely(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))) {
702                 mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
703                 continue;
704             }
705
706             if(unlikely(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))) {
707                 mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
708                 continue;
709             }
710
711             if(unlikely(hash == total_writeback_hash && !strcmp(s, "total_writeback"))) {
712                 mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
713                 continue;
714             }
715
716             if(unlikely(hash == total_dirty_hash && !strcmp(s, "total_dirty"))) {
717                 mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
718                 continue;
719             }
720
721             if(unlikely(hash == total_swap_hash && !strcmp(s, "total_swap"))) {
722                 mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
723                 continue;
724             }
725
726             if(unlikely(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))) {
727                 mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
728                 continue;
729             }
730
731             if(unlikely(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))) {
732                 mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
733                 continue;
734             }
735
736             if(unlikely(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))) {
737                 mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
738                 continue;
739             }
740
741             if(unlikely(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))) {
742                 mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
743                 continue;
744             }
745
746             if(unlikely(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))) {
747                 mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
748                 continue;
749             }
750
751             if(unlikely(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))) {
752                 mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
753                 continue;
754             }
755
756             if(unlikely(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))) {
757                 mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
758                 continue;
759             }
760
761             if(unlikely(hash == total_active_file_hash && !strcmp(s, "total_active_file"))) {
762                 mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
763                 continue;
764             }
765
766             if(unlikely(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))) {
767                 mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
768                 continue;
769             }
770 */
771         }
772
773         // 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);
774
775         mem->updated = 1;
776     }
777
778 memory_next:
779     if(likely(mem->filename_usage_in_bytes)) {
780         if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes)))
781             mem->usage_in_bytes_updated = 1;
782         else
783             mem->usage_in_bytes_updated = 0;
784     }
785
786     if(likely(mem->filename_msw_usage_in_bytes)) {
787         if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes)))
788             mem->msw_usage_in_bytes_updated = 1;
789         else
790             mem->msw_usage_in_bytes_updated = 0;
791     }
792
793     if(likely(mem->filename_failcnt)) {
794         if(unlikely(mem->failcnt_delay_counter > 0)) {
795             mem->failcnt_updated = 0;
796             mem->failcnt_delay_counter--;
797         }
798         else if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt))) {
799             mem->failcnt_updated = 1;
800
801             if(unlikely(!mem->failcnt))
802                 mem->failcnt_delay_counter = cgroup_recheck_zero_mem_failcnt_every_iterations;
803         }
804     }
805 }
806
807 static inline void cgroup_read(struct cgroup *cg) {
808     debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
809
810     cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
811     cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
812     cgroup_read_memory(&cg->memory);
813     cgroup_read_blkio(&cg->io_service_bytes);
814     cgroup_read_blkio(&cg->io_serviced);
815     cgroup_read_blkio(&cg->throttle_io_service_bytes);
816     cgroup_read_blkio(&cg->throttle_io_serviced);
817     cgroup_read_blkio(&cg->io_merged);
818     cgroup_read_blkio(&cg->io_queued);
819 }
820
821 static inline void read_all_cgroups(struct cgroup *root) {
822     debug(D_CGROUP, "reading metrics for all cgroups");
823
824     struct cgroup *cg;
825
826     for(cg = root; cg ; cg = cg->next)
827         if(cg->enabled && cg->available)
828             cgroup_read(cg);
829 }
830
831 // ----------------------------------------------------------------------------
832 // add/remove/find cgroup objects
833
834 #define CGROUP_CHARTID_LINE_MAX 1024
835
836 static inline char *cgroup_title_strdupz(const char *s) {
837     if(!s || !*s) s = "/";
838
839     if(*s == '/' && s[1] != '\0') s++;
840
841     char *r = strdupz(s);
842     netdata_fix_chart_name(r);
843
844     return r;
845 }
846
847 static inline char *cgroup_chart_id_strdupz(const char *s) {
848     if(!s || !*s) s = "/";
849
850     if(*s == '/' && s[1] != '\0') s++;
851
852     char *r = strdupz(s);
853     netdata_fix_chart_id(r);
854
855     return r;
856 }
857
858 static inline void cgroup_get_chart_name(struct cgroup *cg) {
859     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);
860
861     pid_t cgroup_pid;
862     char buffer[CGROUP_CHARTID_LINE_MAX + 1];
863
864     snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", cgroups_rename_script, cg->chart_id);
865
866     debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
867     FILE *fp = mypopen(buffer, &cgroup_pid);
868     if(fp) {
869         // debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
870         char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
871         // debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
872         mypclose(fp, cgroup_pid);
873         // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
874
875         if(s && *s && *s != '\n') {
876             debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
877
878             trim(s);
879
880             freez(cg->chart_title);
881             cg->chart_title = cgroup_title_strdupz(s);
882
883             freez(cg->chart_id);
884             cg->chart_id = cgroup_chart_id_strdupz(s);
885             cg->hash_chart = simple_hash(cg->chart_id);
886         }
887     }
888     else
889         error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
890 }
891
892 static inline struct cgroup *cgroup_add(const char *id) {
893     if(!id || !*id) id = "/";
894     debug(D_CGROUP, "adding to list, cgroup with id '%s'", id);
895
896     if(cgroup_root_count >= cgroup_root_max) {
897         info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
898         return NULL;
899     }
900
901     int def = simple_pattern_matches(disabled_cgroups_patterns, id)?0:cgroup_enable_new_cgroups_detected_at_runtime;
902     struct cgroup *cg = callocz(1, sizeof(struct cgroup));
903
904     cg->id = strdupz(id);
905     cg->hash = simple_hash(cg->id);
906
907     cg->chart_title = cgroup_title_strdupz(id);
908
909     cg->chart_id = cgroup_chart_id_strdupz(id);
910     cg->hash_chart = simple_hash(cg->chart_id);
911
912     if(!cgroup_root)
913         cgroup_root = cg;
914     else {
915         // append it
916         struct cgroup *e;
917         for(e = cgroup_root; e->next ;e = e->next) ;
918         e->next = cg;
919     }
920
921     cgroup_root_count++;
922
923     // fix the chart_id and title by calling the external script
924     if(!simple_pattern_matches(disabled_cgroup_renames, cg->id) &&
925        !simple_pattern_matches(disabled_cgroup_renames, cg->chart_id)) {
926
927         cgroup_get_chart_name(cg);
928
929         debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
930     }
931     else
932         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);
933
934     int user_configurable = 1;
935
936     // check if this cgroup should be a systemd service
937     if(cgroup_enable_systemd_services) {
938         if(simple_pattern_matches(systemd_services_cgroups, cg->id) ||
939                 simple_pattern_matches(systemd_services_cgroups, cg->chart_id)) {
940             debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
941
942             char buffer[CGROUP_CHARTID_LINE_MAX + 1];
943             cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
944
945             strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
946             char *s = buffer;
947
948             //freez(cg->chart_id);
949             //cg->chart_id = cgroup_chart_id_strdupz(s);
950             //cg->hash_chart = simple_hash(cg->chart_id);
951
952             // skip to the last slash
953             size_t len = strlen(s);
954             while(len--) if(unlikely(s[len] == '/')) break;
955             if(len) s = &s[len + 1];
956
957             // remove extension
958             len = strlen(s);
959             while(len--) if(unlikely(s[len] == '.')) break;
960             if(len) s[len] = '\0';
961
962             freez(cg->chart_title);
963             cg->chart_title = cgroup_title_strdupz(s);
964
965             cg->enabled = 1;
966             user_configurable = 0;
967
968             debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
969         }
970         else
971             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);
972     }
973
974     if(user_configurable) {
975         // allow the user to enable/disable this individualy
976         char option[FILENAME_MAX + 1];
977         snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
978         cg->enabled = (char) config_get_boolean("plugin:cgroups", option, def);
979     }
980
981     // detect duplicate cgroups
982     if(cg->enabled) {
983         struct cgroup *t;
984         for (t = cgroup_root; t; t = t->next) {
985             if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
986                 if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
987                     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'.",
988                           cg->chart_id, t->id, cg->id, t->id);
989                     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'.",
990                           cg->chart_id, t->id, cg->id, t->id);
991                     t->enabled = 0;
992                     t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
993                 }
994                 else {
995                     error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
996                           cg->chart_id, t->id, cg->id);
997                     debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
998                           cg->chart_id, t->id, cg->id);
999                     cg->enabled = 0;
1000                     cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
1001                 }
1002
1003                 break;
1004             }
1005         }
1006     }
1007
1008     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");
1009
1010     return cg;
1011 }
1012
1013 static inline void cgroup_free(struct cgroup *cg) {
1014     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");
1015
1016     freez(cg->cpuacct_usage.cpu_percpu);
1017
1018     freez(cg->cpuacct_stat.filename);
1019     freez(cg->cpuacct_usage.filename);
1020
1021     freez(cg->memory.filename);
1022     freez(cg->memory.filename_failcnt);
1023     freez(cg->memory.filename_usage_in_bytes);
1024     freez(cg->memory.filename_msw_usage_in_bytes);
1025
1026     freez(cg->io_service_bytes.filename);
1027     freez(cg->io_serviced.filename);
1028
1029     freez(cg->throttle_io_service_bytes.filename);
1030     freez(cg->throttle_io_serviced.filename);
1031
1032     freez(cg->io_merged.filename);
1033     freez(cg->io_queued.filename);
1034
1035     freez(cg->id);
1036     freez(cg->chart_id);
1037     freez(cg->chart_title);
1038
1039     freez(cg);
1040
1041     cgroup_root_count--;
1042 }
1043
1044 // find if a given cgroup exists
1045 static inline struct cgroup *cgroup_find(const char *id) {
1046     debug(D_CGROUP, "searching for cgroup '%s'", id);
1047
1048     uint32_t hash = simple_hash(id);
1049
1050     struct cgroup *cg;
1051     for(cg = cgroup_root; cg ; cg = cg->next) {
1052         if(hash == cg->hash && strcmp(id, cg->id) == 0)
1053             break;
1054     }
1055
1056     debug(D_CGROUP, "cgroup '%s' %s in memory", id, (cg)?"found":"not found");
1057     return cg;
1058 }
1059
1060 // ----------------------------------------------------------------------------
1061 // detect running cgroups
1062
1063 // callback for find_file_in_subdirs()
1064 static inline void found_subdir_in_dir(const char *dir) {
1065     debug(D_CGROUP, "examining cgroup dir '%s'", dir);
1066
1067     struct cgroup *cg = cgroup_find(dir);
1068     if(!cg) {
1069         if(*dir && cgroup_max_depth > 0) {
1070             int depth = 0;
1071             const char *s;
1072
1073             for(s = dir; *s ;s++)
1074                 if(unlikely(*s == '/'))
1075                     depth++;
1076
1077             if(depth > cgroup_max_depth) {
1078                 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
1079                 return;
1080             }
1081         }
1082         // debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
1083         cg = cgroup_add(dir);
1084     }
1085
1086     if(cg) cg->available = 1;
1087 }
1088
1089 static inline int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
1090     if(!this) this = base;
1091     debug(D_CGROUP, "searching for directories in '%s' (base '%s')", this?this:"", base);
1092
1093     size_t dirlen = strlen(this), baselen = strlen(base);
1094
1095     int ret = -1;
1096     int enabled = -1;
1097
1098     const char *relative_path = &this[baselen];
1099     if(!*relative_path) relative_path = "/";
1100
1101     DIR *dir = opendir(this);
1102     if(!dir) {
1103         error("Cannot read cgroups directory '%s'", base);
1104         return ret;
1105     }
1106     ret = 1;
1107
1108     callback(relative_path);
1109
1110     struct dirent *de = NULL;
1111     while((de = readdir(dir))) {
1112         if(de->d_type == DT_DIR
1113             && (
1114                 (de->d_name[0] == '.' && de->d_name[1] == '\0')
1115                 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
1116                 ))
1117             continue;
1118
1119         if(de->d_type == DT_DIR) {
1120             if(enabled == -1) {
1121                 const char *r = relative_path;
1122                 if(*r == '\0') r = "/";
1123
1124                 // do not decent in directories we are not interested
1125                 int def = 1;
1126                 if(simple_pattern_matches(disabled_cgroup_paths, r))
1127                     def = 0;
1128
1129                 // we check for this option here
1130                 // so that the config will not have settings
1131                 // for leaf directories
1132                 char option[FILENAME_MAX + 1];
1133                 snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
1134                 option[FILENAME_MAX] = '\0';
1135                 enabled = config_get_boolean("plugin:cgroups", option, def);
1136             }
1137
1138             if(enabled) {
1139                 char *s = mallocz(dirlen + strlen(de->d_name) + 2);
1140                 strcpy(s, this);
1141                 strcat(s, "/");
1142                 strcat(s, de->d_name);
1143                 int ret2 = find_dir_in_subdirs(base, s, callback);
1144                 if(ret2 > 0) ret += ret2;
1145                 freez(s);
1146             }
1147         }
1148     }
1149
1150     closedir(dir);
1151     return ret;
1152 }
1153
1154 static inline void mark_all_cgroups_as_not_available() {
1155     debug(D_CGROUP, "marking all cgroups as not available");
1156
1157     struct cgroup *cg;
1158
1159     // mark all as not available
1160     for(cg = cgroup_root; cg ; cg = cg->next) {
1161         cg->available = 0;
1162     }
1163 }
1164
1165 static inline void cleanup_all_cgroups() {
1166     struct cgroup *cg = cgroup_root, *last = NULL;
1167
1168     for(; cg ;) {
1169         if(!cg->available) {
1170             // enable the first duplicate cgroup
1171             {
1172                 struct cgroup *t;
1173                 for(t = cgroup_root; t ; t = t->next) {
1174                     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)) {
1175                         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);
1176                         t->enabled = 1;
1177                         t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1178                         break;
1179                     }
1180                 }
1181             }
1182
1183             if(!last)
1184                 cgroup_root = cg->next;
1185             else
1186                 last->next = cg->next;
1187
1188             cgroup_free(cg);
1189
1190             if(!last)
1191                 cg = cgroup_root;
1192             else
1193                 cg = last->next;
1194         }
1195         else {
1196             last = cg;
1197             cg = cg->next;
1198         }
1199     }
1200 }
1201
1202 static inline void find_all_cgroups() {
1203     debug(D_CGROUP, "searching for cgroups");
1204
1205     mark_all_cgroups_as_not_available();
1206
1207     if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
1208         if (find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
1209             cgroup_enable_cpuacct_stat = cgroup_enable_cpuacct_usage = 0;
1210             error("disabled cgroup cpu statistics.");
1211         }
1212     }
1213
1214     if(cgroup_enable_blkio) {
1215         if (find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
1216             cgroup_enable_blkio = 0;
1217             error("disabled cgroup blkio statistics.");
1218         }
1219     }
1220
1221     if(cgroup_enable_memory) {
1222         if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
1223             cgroup_enable_memory = 0;
1224             error("disabled cgroup memory statistics.");
1225         }
1226     }
1227
1228     if(cgroup_enable_devices) {
1229         if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
1230             cgroup_enable_devices = 0;
1231             error("disabled cgroup devices statistics.");
1232         }
1233     }
1234
1235     // remove any non-existing cgroups
1236     cleanup_all_cgroups();
1237
1238     struct cgroup *cg;
1239     struct stat buf;
1240     for(cg = cgroup_root; cg ; cg = cg->next) {
1241         // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
1242
1243         if(unlikely(!cg->available))
1244             continue;
1245
1246         debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
1247
1248         // check for newly added cgroups
1249         // and update the filenames they read
1250         char filename[FILENAME_MAX + 1];
1251         if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
1252             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
1253             if(likely(stat(filename, &buf) != -1)) {
1254                 cg->cpuacct_stat.filename = strdupz(filename);
1255                 debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
1256             }
1257             else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1258         }
1259
1260         if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))) {
1261             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
1262             if(likely(stat(filename, &buf) != -1)) {
1263                 cg->cpuacct_usage.filename = strdupz(filename);
1264                 debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
1265             }
1266             else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1267         }
1268
1269         if(unlikely(cgroup_enable_memory)) {
1270             if(unlikely(!cg->memory.filename) && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) {
1271                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
1272                 if(likely(stat(filename, &buf) != -1)) {
1273                     cg->memory.filename = strdupz(filename);
1274                     debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
1275                 }
1276                 else
1277                     debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1278             }
1279             if(unlikely(!cg->memory.filename_usage_in_bytes)) {
1280                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
1281                 if(likely(stat(filename, &buf) != -1)) {
1282                     cg->memory.filename_usage_in_bytes = strdupz(filename);
1283                     debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
1284                 }
1285                 else
1286                     debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1287             }
1288             if(unlikely(!cg->memory.filename_msw_usage_in_bytes)) {
1289                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
1290                 if(likely(stat(filename, &buf) != -1)) {
1291                     cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
1292                     debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
1293                 }
1294                 else
1295                     debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1296             }
1297             if(unlikely(!cg->memory.filename_failcnt)) {
1298                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
1299                 if(likely(stat(filename, &buf) != -1)) {
1300                     cg->memory.filename_failcnt = strdupz(filename);
1301                     debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
1302                 }
1303                 else
1304                     debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1305             }
1306         }
1307
1308         if(unlikely(cgroup_enable_blkio)) {
1309             if(unlikely(!cg->io_service_bytes.filename)) {
1310                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
1311                 if(likely(stat(filename, &buf) != -1)) {
1312                     cg->io_service_bytes.filename = strdupz(filename);
1313                     debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
1314                 }
1315                 else
1316                     debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1317             }
1318
1319             if(unlikely(!cg->io_serviced.filename)) {
1320                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
1321                 if(likely(stat(filename, &buf) != -1)) {
1322                     cg->io_serviced.filename = strdupz(filename);
1323                     debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
1324                 }
1325                 else
1326                     debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1327             }
1328
1329             if(unlikely(!cg->throttle_io_service_bytes.filename)) {
1330                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
1331                 if(likely(stat(filename, &buf) != -1)) {
1332                     cg->throttle_io_service_bytes.filename = strdupz(filename);
1333                     debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
1334                 }
1335                 else
1336                     debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1337             }
1338
1339             if(unlikely(!cg->throttle_io_serviced.filename)) {
1340                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
1341                 if(likely(stat(filename, &buf) != -1)) {
1342                     cg->throttle_io_serviced.filename = strdupz(filename);
1343                     debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
1344                 }
1345                 else
1346                     debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1347             }
1348
1349             if(unlikely(!cg->io_merged.filename)) {
1350                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
1351                 if(likely(stat(filename, &buf) != -1)) {
1352                     cg->io_merged.filename = strdupz(filename);
1353                     debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
1354                 }
1355                 else
1356                     debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1357             }
1358
1359             if(unlikely(!cg->io_queued.filename)) {
1360                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
1361                 if(likely(stat(filename, &buf) != -1)) {
1362                     cg->io_queued.filename = strdupz(filename);
1363                     debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
1364                 }
1365                 else
1366                     debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1367             }
1368         }
1369     }
1370
1371     debug(D_CGROUP, "done searching for cgroups");
1372     return;
1373 }
1374
1375 // ----------------------------------------------------------------------------
1376 // generate charts
1377
1378 #define CHART_TITLE_MAX 300
1379
1380 void update_services_charts(int update_every,
1381         int do_cpu,
1382         int do_mem_usage,
1383         int do_mem_failcnt,
1384         int do_swap_usage,
1385         int do_io,
1386         int do_io_ops,
1387         int do_throttle_io,
1388         int do_throttle_ops,
1389         int do_queued_ops,
1390         int do_merged_ops
1391 ) {
1392     static RRDSET
1393             *st_cpu = NULL,
1394             *st_mem_usage = NULL,
1395             *st_mem_failcnt = NULL,
1396             *st_swap_usage = NULL,
1397
1398             *st_io_read = NULL,
1399             *st_io_serviced_read = NULL,
1400             *st_throttle_io_read = NULL,
1401             *st_throttle_ops_read = NULL,
1402             *st_queued_ops_read = NULL,
1403             *st_merged_ops_read = NULL,
1404
1405             *st_io_write = NULL,
1406             *st_io_serviced_write = NULL,
1407             *st_throttle_io_write = NULL,
1408             *st_throttle_ops_write = NULL,
1409             *st_queued_ops_write = NULL,
1410             *st_merged_ops_write = NULL;
1411
1412     // create the charts
1413
1414     if(likely(do_cpu)) {
1415         if(unlikely(!st_cpu)) {
1416             char title[CHART_TITLE_MAX + 1];
1417
1418             st_cpu = rrdset_find_bytype("services", "cpu");
1419             if(likely(!st_cpu)) {
1420                 snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : "");
1421                 st_cpu = rrdset_create("services", "cpu", NULL, "cpu", "services.cpu", title, "%", 30000, update_every, RRDSET_TYPE_STACKED);
1422             }
1423         }
1424         else
1425             rrdset_next(st_cpu);
1426     }
1427
1428     if(likely(do_mem_usage)) {
1429         if(unlikely(!st_mem_usage)) {
1430             st_mem_usage = rrdset_find_bytype("services", "mem_usage");
1431             if(likely(!st_mem_usage))
1432                 st_mem_usage = rrdset_create("services", "mem_usage", NULL, "mem", "services.mem_usage", "Systemd Services Used RAM with Cache", "MB", 30001, update_every, RRDSET_TYPE_STACKED);
1433         }
1434         else
1435             rrdset_next(st_mem_usage);
1436     }
1437
1438     if(likely(do_mem_failcnt)) {
1439         if(unlikely(!st_mem_failcnt)) {
1440             st_mem_failcnt = rrdset_find_bytype("services", "mem_failcnt");
1441             if(likely(!st_mem_failcnt))
1442                 st_mem_failcnt = rrdset_create("services", "mem_failcnt", NULL, "mem", "services.mem_failcnt", "Systemd Services Memory Limit Failures", "MB", 30003, update_every, RRDSET_TYPE_STACKED);
1443         }
1444         else
1445             rrdset_next(st_mem_failcnt);
1446     }
1447
1448     if(likely(do_swap_usage)) {
1449         if(unlikely(!st_swap_usage)) {
1450             st_swap_usage = rrdset_find_bytype("services", "swap_usage");
1451             if(likely(!st_swap_usage))
1452                 st_swap_usage = rrdset_create("services", "swap_usage", NULL, "swap", "services.swap_usage", "Systemd Services Swap Memory Used", "MB", 30002, update_every, RRDSET_TYPE_STACKED);
1453         }
1454         else
1455             rrdset_next(st_swap_usage);
1456     }
1457
1458     if(likely(do_io)) {
1459         if(unlikely(!st_io_read)) {
1460             st_io_read = rrdset_find_bytype("services", "io_read");
1461             if(likely(!st_io_read))
1462                 st_io_read = rrdset_create("services", "io_read", NULL, "disk", "services.io_read", "Systemd Services Disk Read Bandwidth", "KB/s", 30012, update_every, RRDSET_TYPE_STACKED);
1463         }
1464         else
1465             rrdset_next(st_io_read);
1466
1467         if(unlikely(!st_io_write)) {
1468             st_io_write = rrdset_find_bytype("services", "io_write");
1469             if(likely(!st_io_write))
1470                 st_io_write = rrdset_create("services", "io_write", NULL, "disk", "services.io_write", "Systemd Services Disk Write Bandwidth", "KB/s", 30013, update_every, RRDSET_TYPE_STACKED);
1471         }
1472         else
1473             rrdset_next(st_io_write);
1474     }
1475
1476     if(likely(do_io_ops)) {
1477         if(unlikely(!st_io_serviced_read)) {
1478             st_io_serviced_read = rrdset_find_bytype("services", "io_ops_read");
1479             if(likely(!st_io_serviced_read))
1480                 st_io_serviced_read = rrdset_create("services", "io_ops_read", NULL, "disk", "services.io_ops_read", "Systemd Services Disk Read Operations", "operations/s", 30014, update_every, RRDSET_TYPE_STACKED);
1481         }
1482         else
1483             rrdset_next(st_io_serviced_read);
1484
1485         if(unlikely(!st_io_serviced_write)) {
1486             st_io_serviced_write = rrdset_find_bytype("services", "io_ops_write");
1487             if(likely(!st_io_serviced_write))
1488                 st_io_serviced_write = rrdset_create("services", "io_ops_write", NULL, "disk", "services.io_ops_write", "Systemd Services Disk Write Operations", "operations/s", 30015, update_every, RRDSET_TYPE_STACKED);
1489         }
1490         else
1491             rrdset_next(st_io_serviced_write);
1492     }
1493
1494     if(likely(do_throttle_io)) {
1495         if(unlikely(!st_throttle_io_read)) {
1496             st_throttle_io_read = rrdset_find_bytype("services", "throttle_io_read");
1497             if(likely(!st_throttle_io_read))
1498                 st_throttle_io_read = rrdset_create("services", "throttle_io_read", NULL, "disk", "services.throttle_io_read", "Systemd Services Throttle Disk Read Bandwidth", "KB/s", 30016, update_every, RRDSET_TYPE_STACKED);
1499         }
1500         else
1501             rrdset_next(st_throttle_io_read);
1502
1503         if(unlikely(!st_throttle_io_write)) {
1504             st_throttle_io_write = rrdset_find_bytype("services", "throttle_io_write");
1505             if(likely(!st_throttle_io_write))
1506                 st_throttle_io_write = rrdset_create("services", "throttle_io_write", NULL, "disk", "services.throttle_io_write", "Systemd Services Throttle Disk Write Bandwidth", "KB/s", 30017, update_every, RRDSET_TYPE_STACKED);
1507         }
1508         else
1509             rrdset_next(st_throttle_io_write);
1510     }
1511
1512     if(likely(do_throttle_ops)) {
1513         if(unlikely(!st_throttle_ops_read)) {
1514             st_throttle_ops_read = rrdset_find_bytype("services", "throttle_io_ops_read");
1515             if(likely(!st_throttle_ops_read))
1516                 st_throttle_ops_read = rrdset_create("services", "throttle_io_ops_read", NULL, "disk", "services.throttle_io_ops_read", "Systemd Services Throttle Disk Read Operations", "operations/s", 30018, update_every, RRDSET_TYPE_STACKED);
1517         }
1518         else
1519             rrdset_next(st_throttle_ops_read);
1520
1521         if(unlikely(!st_throttle_ops_write)) {
1522             st_throttle_ops_write = rrdset_find_bytype("services", "throttle_io_ops_write");
1523             if(likely(!st_throttle_ops_write))
1524                 st_throttle_ops_write = rrdset_create("services", "throttle_io_ops_write", NULL, "disk", "services.throttle_io_ops_write", "Systemd Services Throttle Disk Write Operations", "operations/s", 30019, update_every, RRDSET_TYPE_STACKED);
1525         }
1526         else
1527             rrdset_next(st_throttle_ops_write);
1528     }
1529
1530     if(likely(do_queued_ops)) {
1531         if(unlikely(!st_queued_ops_read)) {
1532             st_queued_ops_read = rrdset_find_bytype("services", "queued_io_ops_read");
1533             if(likely(!st_queued_ops_read))
1534                 st_queued_ops_read = rrdset_create("services", "queued_io_ops_read", NULL, "disk", "services.queued_io_ops_read", "Systemd Services Queued Disk Read Operations", "operations/s", 30020, update_every, RRDSET_TYPE_STACKED);
1535         }
1536         else
1537             rrdset_next(st_queued_ops_read);
1538
1539         if(unlikely(!st_queued_ops_write)) {
1540             st_queued_ops_write = rrdset_find_bytype("services", "queued_io_ops_write");
1541             if(likely(!st_queued_ops_write))
1542                 st_queued_ops_write = rrdset_create("services", "queued_io_ops_write", NULL, "disk", "services.queued_io_ops_write", "Systemd Services Queued Disk Write Operations", "operations/s", 30021, update_every, RRDSET_TYPE_STACKED);
1543         }
1544         else
1545             rrdset_next(st_queued_ops_write);
1546     }
1547
1548     if(likely(do_merged_ops)) {
1549         if(unlikely(!st_merged_ops_read)) {
1550             st_merged_ops_read = rrdset_find_bytype("services", "merged_io_ops_read");
1551             if(likely(!st_merged_ops_read))
1552                 st_merged_ops_read = rrdset_create("services", "merged_io_ops_read", NULL, "disk", "services.merged_io_ops_read", "Systemd Services Merged Disk Read Operations", "operations/s", 30022, update_every, RRDSET_TYPE_STACKED);
1553         }
1554         else
1555             rrdset_next(st_merged_ops_read);
1556
1557         if(unlikely(!st_merged_ops_write)) {
1558             st_merged_ops_write = rrdset_find_bytype("services", "merged_io_ops_write");
1559             if(likely(!st_merged_ops_write))
1560                 st_merged_ops_write = rrdset_create("services", "merged_io_ops_write", NULL, "disk", "services.merged_io_ops_write", "Systemd Services Merged Disk Write Operations", "operations/s", 30023, update_every, RRDSET_TYPE_STACKED);
1561         }
1562         else
1563             rrdset_next(st_merged_ops_write);
1564     }
1565
1566     // update the values
1567     struct cgroup *cg;
1568     for(cg = cgroup_root; cg ; cg = cg->next) {
1569         if(unlikely(!cg->available || !cg->enabled || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))
1570             continue;
1571
1572         if(likely(do_cpu && cg->cpuacct_stat.updated)) {
1573             if(unlikely(!cg->rd_cpu))
1574                 cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRDDIM_INCREMENTAL);
1575
1576             rrddim_set_by_pointer(st_cpu, cg->rd_cpu, cg->cpuacct_stat.user + cg->cpuacct_stat.system);
1577         }
1578
1579         if(likely(do_mem_usage && cg->memory.usage_in_bytes_updated)) {
1580             if(unlikely(!cg->rd_mem_usage))
1581                 cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1582
1583             rrddim_set_by_pointer(st_mem_usage, cg->rd_mem_usage, cg->memory.usage_in_bytes);
1584         }
1585
1586         if(likely(do_mem_failcnt && cg->memory.failcnt_updated)) {
1587             if(unlikely(!cg->rd_mem_failcnt))
1588                 cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1589
1590             rrddim_set_by_pointer(st_mem_failcnt, cg->rd_mem_failcnt, cg->memory.failcnt);
1591         }
1592
1593         if(likely(do_swap_usage && cg->memory.msw_usage_in_bytes_updated)) {
1594             if(unlikely(!cg->rd_swap_usage))
1595                 cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1596
1597             rrddim_set_by_pointer(st_swap_usage, cg->rd_swap_usage, cg->memory.msw_usage_in_bytes);
1598         }
1599
1600         if(likely(do_io && cg->io_service_bytes.updated)) {
1601             if(unlikely(!cg->rd_io_service_bytes_read))
1602                 cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
1603
1604             rrddim_set_by_pointer(st_io_read, cg->rd_io_service_bytes_read, cg->io_service_bytes.Read);
1605
1606             if(unlikely(!cg->rd_io_service_bytes_write))
1607                 cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
1608
1609             rrddim_set_by_pointer(st_io_write, cg->rd_io_service_bytes_write, cg->io_service_bytes.Write);
1610         }
1611
1612         if(likely(do_io_ops && cg->io_serviced.updated)) {
1613             if(unlikely(!cg->rd_io_serviced_read))
1614                 cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1615
1616             rrddim_set_by_pointer(st_io_serviced_read, cg->rd_io_serviced_read, cg->io_serviced.Read);
1617
1618             if(unlikely(!cg->rd_io_serviced_write))
1619                 cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1620
1621             rrddim_set_by_pointer(st_io_serviced_write, cg->rd_io_serviced_write, cg->io_serviced.Write);
1622         }
1623
1624         if(likely(do_throttle_io && cg->throttle_io_service_bytes.updated)) {
1625             if(unlikely(!cg->rd_throttle_io_read))
1626                 cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
1627
1628             rrddim_set_by_pointer(st_throttle_io_read, cg->rd_throttle_io_read, cg->throttle_io_service_bytes.Read);
1629
1630             if(unlikely(!cg->rd_throttle_io_write))
1631                 cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
1632
1633             rrddim_set_by_pointer(st_throttle_io_write, cg->rd_throttle_io_write, cg->throttle_io_service_bytes.Write);
1634         }
1635
1636         if(likely(do_throttle_ops && cg->throttle_io_serviced.updated)) {
1637             if(unlikely(!cg->rd_throttle_io_serviced_read))
1638                 cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1639
1640             rrddim_set_by_pointer(st_throttle_ops_read, cg->rd_throttle_io_serviced_read, cg->throttle_io_serviced.Read);
1641
1642             if(unlikely(!cg->rd_throttle_io_serviced_write))
1643                 cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1644
1645             rrddim_set_by_pointer(st_throttle_ops_write, cg->rd_throttle_io_serviced_write, cg->throttle_io_serviced.Write);
1646         }
1647
1648         if(likely(do_queued_ops && cg->io_queued.updated)) {
1649             if(unlikely(!cg->rd_io_queued_read))
1650                 cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1651
1652             rrddim_set_by_pointer(st_queued_ops_read, cg->rd_io_queued_read, cg->io_queued.Read);
1653
1654             if(unlikely(!cg->rd_io_queued_write))
1655                 cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1656
1657             rrddim_set_by_pointer(st_queued_ops_write, cg->rd_io_queued_write, cg->io_queued.Write);
1658         }
1659
1660         if(likely(do_merged_ops && cg->io_merged.updated)) {
1661             if(unlikely(!cg->rd_io_merged_read))
1662                 cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1663
1664             rrddim_set_by_pointer(st_merged_ops_read, cg->rd_io_merged_read, cg->io_merged.Read);
1665
1666             if(unlikely(!cg->rd_io_merged_write))
1667                 cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
1668
1669             rrddim_set_by_pointer(st_merged_ops_write, cg->rd_io_merged_write, cg->io_merged.Write);
1670         }
1671     }
1672
1673     // complete the iteration
1674     if(likely(do_cpu))
1675         rrdset_done(st_cpu);
1676
1677     if(likely(do_mem_usage))
1678         rrdset_done(st_mem_usage);
1679
1680     if(likely(do_mem_failcnt))
1681         rrdset_done(st_mem_failcnt);
1682
1683     if(likely(do_swap_usage))
1684         rrdset_done(st_swap_usage);
1685
1686     if(likely(do_io)) {
1687         rrdset_done(st_io_read);
1688         rrdset_done(st_io_write);
1689     }
1690
1691     if(likely(do_io_ops)) {
1692         rrdset_done(st_io_serviced_read);
1693         rrdset_done(st_io_serviced_write);
1694     }
1695
1696     if(likely(do_throttle_io)) {
1697         rrdset_done(st_throttle_io_read);
1698         rrdset_done(st_throttle_io_write);
1699     }
1700
1701     if(likely(do_throttle_ops)) {
1702         rrdset_done(st_throttle_ops_read);
1703         rrdset_done(st_throttle_ops_write);
1704     }
1705 }
1706
1707 static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) {
1708     if(buffer[0]) return buffer;
1709
1710     if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0'))
1711         strncpy(buffer, "cgroup_root", len);
1712     else
1713         snprintfz(buffer, len, "cgroup_%s", id);
1714
1715     netdata_fix_chart_id(buffer);
1716     return buffer;
1717 }
1718
1719 void update_cgroup_charts(int update_every) {
1720     debug(D_CGROUP, "updating cgroups charts");
1721
1722     char type[RRD_ID_LENGTH_MAX + 1];
1723     char title[CHART_TITLE_MAX + 1];
1724
1725     int services_do_cpu = 0,
1726             services_do_mem_usage = 0,
1727             services_do_mem_failcnt = 0,
1728             services_do_swap_usage = 0,
1729             services_do_io = 0,
1730             services_do_io_ops = 0,
1731             services_do_throttle_io = 0,
1732             services_do_throttle_ops = 0,
1733             services_do_queued_ops = 0,
1734             services_do_merged_ops = 0;
1735
1736     struct cgroup *cg;
1737     for(cg = cgroup_root; cg ; cg = cg->next) {
1738         if(unlikely(!cg->available || !cg->enabled))
1739             continue;
1740
1741         if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) {
1742             if(cg->cpuacct_stat.updated && (cg->cpuacct_stat.user || cg->cpuacct_stat.system)) services_do_cpu++;
1743             if(cg->memory.usage_in_bytes_updated && (cg->memory.usage_in_bytes)) services_do_mem_usage++;
1744             if(cg->memory.failcnt_updated && (cg->memory.failcnt)) services_do_mem_failcnt++;
1745             if(cg->memory.msw_usage_in_bytes_updated && (cg->memory.msw_usage_in_bytes)) services_do_swap_usage++;
1746             if(cg->io_service_bytes.updated && (cg->io_service_bytes.Read || cg->io_service_bytes.Write)) services_do_io++;
1747             if(cg->io_serviced.updated && (cg->io_serviced.Read || cg->io_serviced.Write)) services_do_io_ops++;
1748             if(cg->throttle_io_service_bytes.updated && (cg->throttle_io_service_bytes.Read || cg->throttle_io_service_bytes.Write)) services_do_throttle_io++;
1749             if(cg->throttle_io_serviced.updated && (cg->throttle_io_serviced.Read || cg->throttle_io_serviced.Write)) services_do_throttle_ops++;
1750             if(cg->io_queued.updated && (cg->io_queued.Read || cg->io_queued.Write)) services_do_queued_ops++;
1751             if(cg->io_merged.updated && (cg->io_merged.Read || cg->io_merged.Write)) services_do_merged_ops++;
1752             continue;
1753         }
1754
1755         type[0] = '\0';
1756
1757         if(likely(cg->cpuacct_stat.updated)) {
1758             if(unlikely(!cg->st_cpu)) {
1759                 cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu");
1760                 if(likely(!cg->st_cpu)) {
1761                     snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
1762                     cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
1763                 }
1764                 rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
1765                 rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
1766             }
1767             else
1768                 rrdset_next(cg->st_cpu);
1769
1770             rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user);
1771             rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system);
1772             rrdset_done(cg->st_cpu);
1773         }
1774
1775         if(likely(cg->cpuacct_usage.updated)) {
1776             char id[RRD_ID_LENGTH_MAX + 1];
1777             unsigned int i;
1778
1779             if(unlikely(!cg->st_cpu_per_core)) {
1780                 cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core");
1781                 if(likely(!cg->st_cpu_per_core)) {
1782                     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);
1783                     cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
1784                 }
1785                 for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
1786                     snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1787                     rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
1788                 }
1789             }
1790             else
1791                 rrdset_next(cg->st_cpu_per_core);
1792
1793             for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
1794                 snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1795                 rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]);
1796             }
1797             rrdset_done(cg->st_cpu_per_core);
1798         }
1799
1800         if(likely(cg->memory.updated)) {
1801             if(likely(cg->memory.cache || cg->memory.rss || cg->memory.rss_huge || cg->memory.mapped_file)) {
1802                 if(unlikely(!cg->st_mem)) {
1803                     cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem");
1804                     if(likely(!cg->st_mem)) {
1805                         snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
1806                         cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every, RRDSET_TYPE_STACKED);
1807                     }
1808
1809                     rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1810                     rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1811                     if(cg->memory.has_dirty_swap)
1812                         rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1813                     rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1814                     rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1815                 }
1816                 else
1817                     rrdset_next(cg->st_mem);
1818
1819                 rrddim_set(cg->st_mem, "cache", cg->memory.cache);
1820                 rrddim_set(cg->st_mem, "rss", cg->memory.rss);
1821                 if(cg->memory.has_dirty_swap)
1822                     rrddim_set(cg->st_mem, "swap", cg->memory.swap);
1823                 rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge);
1824                 rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file);
1825                 rrdset_done(cg->st_mem);
1826             }
1827
1828             if(unlikely(!cg->st_writeback)) {
1829                 cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback");
1830                 if(likely(!cg->st_writeback)) {
1831                     snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
1832                     cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300, update_every, RRDSET_TYPE_AREA);
1833                 }
1834
1835                 if(cg->memory.has_dirty_swap)
1836                     rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1837                 rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1838             }
1839             else
1840                 rrdset_next(cg->st_writeback);
1841
1842             if(cg->memory.has_dirty_swap)
1843                 rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty);
1844             rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback);
1845             rrdset_done(cg->st_writeback);
1846
1847             if(likely(cg->memory.pgpgin || cg->memory.pgpgout)) {
1848                 if(unlikely(!cg->st_mem_activity)) {
1849                     cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity");
1850                     if(likely(!cg->st_mem_activity)) {
1851                         snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
1852                         cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", 40400, update_every, RRDSET_TYPE_LINE);
1853                     }
1854                     rrddim_add(cg->st_mem_activity, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1855                     rrddim_add(cg->st_mem_activity, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1856                 }
1857                 else
1858                     rrdset_next(cg->st_mem_activity);
1859
1860                 rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.pgpgin);
1861                 rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.pgpgout);
1862                 rrdset_done(cg->st_mem_activity);
1863             }
1864
1865             if(likely(cg->memory.pgfault || cg->memory.pgmajfault)) {
1866                 if(unlikely(!cg->st_pgfaults)) {
1867                     cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults");
1868                     if(likely(!cg->st_pgfaults)) {
1869                         snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
1870                         cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500, update_every, RRDSET_TYPE_LINE);
1871                     }
1872                     rrddim_add(cg->st_pgfaults, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1873                     rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1874                 }
1875                 else
1876                     rrdset_next(cg->st_pgfaults);
1877
1878                 rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.pgfault);
1879                 rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.pgmajfault);
1880                 rrdset_done(cg->st_pgfaults);
1881             }
1882         }
1883
1884         if(likely(cg->memory.usage_in_bytes_updated)) {
1885             if(unlikely(!cg->st_mem_usage)) {
1886                 cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage");
1887                 if(likely(!cg->st_mem_usage)) {
1888                     snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title);
1889                     cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200, update_every, RRDSET_TYPE_STACKED);
1890                 }
1891                 rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1892                 rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1893             }
1894             else
1895                 rrdset_next(cg->st_mem_usage);
1896
1897             rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes);
1898             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);
1899             rrdset_done(cg->st_mem_usage);
1900         }
1901
1902         if(likely(cg->memory.failcnt_updated && cg->memory.failcnt)) {
1903             if(unlikely(!cg->st_mem_failcnt)) {
1904                 cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt");
1905                 if(likely(!cg->st_mem_failcnt)) {
1906                     snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
1907                     cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "count", 40250, update_every, RRDSET_TYPE_LINE);
1908                 }
1909                 rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
1910             }
1911             else
1912                 rrdset_next(cg->st_mem_failcnt);
1913
1914             rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt);
1915             rrdset_done(cg->st_mem_failcnt);
1916         }
1917
1918         if(likely(cg->io_service_bytes.updated && (cg->io_service_bytes.Read || cg->io_service_bytes.Write))) {
1919             if(unlikely(!cg->st_io)) {
1920                 cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io");
1921                 if(likely(!cg->st_io)) {
1922                     snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1923                     cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1924                 }
1925                 rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1926                 rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1927             }
1928             else
1929                 rrdset_next(cg->st_io);
1930
1931             rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read);
1932             rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write);
1933             rrdset_done(cg->st_io);
1934         }
1935
1936         if(likely(cg->io_serviced.updated && (cg->io_serviced.Read || cg->io_serviced.Write))) {
1937             if(unlikely(!cg->st_serviced_ops)) {
1938                 cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops");
1939                 if(likely(!cg->st_serviced_ops)) {
1940                     snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
1941                     cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200, update_every, RRDSET_TYPE_LINE);
1942                 }
1943                 rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1944                 rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1945             }
1946             else
1947                 rrdset_next(cg->st_serviced_ops);
1948
1949             rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read);
1950             rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write);
1951             rrdset_done(cg->st_serviced_ops);
1952         }
1953
1954         if(likely(cg->throttle_io_service_bytes.updated && (cg->throttle_io_service_bytes.Read || cg->throttle_io_service_bytes.Write))) {
1955             if(unlikely(!cg->st_throttle_io)) {
1956                 cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io");
1957                 if(likely(!cg->st_throttle_io)) {
1958                     snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1959                     cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1960                 }
1961                 rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1962                 rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1963             }
1964             else
1965                 rrdset_next(cg->st_throttle_io);
1966
1967             rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read);
1968             rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write);
1969             rrdset_done(cg->st_throttle_io);
1970         }
1971
1972         if(likely(cg->throttle_io_serviced.updated && (cg->throttle_io_serviced.Read || cg->throttle_io_serviced.Write))) {
1973             if(unlikely(!cg->st_throttle_serviced_ops)) {
1974                 cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops");
1975                 if(likely(!cg->st_throttle_serviced_ops)) {
1976                     snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
1977                     cg->st_throttle_serviced_ops = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200, update_every, RRDSET_TYPE_LINE);
1978                 }
1979                 rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1980                 rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1981             }
1982             else
1983                 rrdset_next(cg->st_throttle_serviced_ops);
1984
1985             rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read);
1986             rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write);
1987             rrdset_done(cg->st_throttle_serviced_ops);
1988         }
1989
1990         if(likely(cg->io_queued.updated)) {
1991             if(unlikely(!cg->st_queued_ops)) {
1992                 cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops");
1993                 if(likely(!cg->st_queued_ops)) {
1994                     snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
1995                     cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000, update_every, RRDSET_TYPE_LINE);
1996                 }
1997                 rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
1998                 rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
1999             }
2000             else
2001                 rrdset_next(cg->st_queued_ops);
2002
2003             rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read);
2004             rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write);
2005             rrdset_done(cg->st_queued_ops);
2006         }
2007
2008         if(likely(cg->io_merged.updated && (cg->io_merged.Read || cg->io_merged.Write))) {
2009             if(unlikely(!cg->st_merged_ops)) {
2010                 cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops");
2011                 if(likely(!cg->st_merged_ops)) {
2012                     snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
2013                     cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100, update_every, RRDSET_TYPE_LINE);
2014                 }
2015                 rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
2016                 rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
2017             }
2018             else
2019                 rrdset_next(cg->st_merged_ops);
2020
2021             rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read);
2022             rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write);
2023             rrdset_done(cg->st_merged_ops);
2024         }
2025     }
2026
2027     if(likely(cgroup_enable_systemd_services))
2028         update_services_charts(update_every,
2029                 services_do_cpu,
2030                 services_do_mem_usage,
2031                 services_do_mem_failcnt,
2032                 services_do_swap_usage,
2033                 services_do_io,
2034                 services_do_io_ops,
2035                 services_do_throttle_io,
2036                 services_do_throttle_ops,
2037                 services_do_queued_ops,
2038                 services_do_merged_ops
2039         );
2040
2041     debug(D_CGROUP, "done updating cgroups charts");
2042 }
2043
2044 // ----------------------------------------------------------------------------
2045 // cgroups main
2046
2047 void *cgroups_main(void *ptr) {
2048     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
2049
2050     info("CGROUP Plugin thread created with task id %d", gettid());
2051
2052     if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
2053         error("Cannot set pthread cancel type to DEFERRED.");
2054
2055     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
2056         error("Cannot set pthread cancel state to ENABLE.");
2057
2058     struct rusage thread;
2059
2060     // when ZERO, attempt to do it
2061     int vdo_cpu_netdata = config_get_boolean("plugin:cgroups", "cgroups plugin resource charts", 1);
2062
2063     read_cgroup_plugin_configuration();
2064
2065     RRDSET *stcpu_thread = NULL;
2066
2067     usec_t step = cgroup_update_every * USEC_PER_SEC;
2068     usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_next = 0;
2069     for(;;) {
2070         usec_t now = now_monotonic_usec();
2071         usec_t next = now - (now % step) + step;
2072
2073         while(now < next) {
2074             sleep_usec(next - now);
2075             now = now_monotonic_usec();
2076         }
2077
2078         if(unlikely(netdata_exit)) break;
2079
2080         // BEGIN -- the job to be done
2081
2082         if(unlikely(now >= find_next)) {
2083             find_all_cgroups();
2084             find_next = now + find_every;
2085         }
2086
2087         read_all_cgroups(cgroup_root);
2088         update_cgroup_charts(cgroup_update_every);
2089
2090         // END -- the job is done
2091
2092         // --------------------------------------------------------------------
2093
2094         if(vdo_cpu_netdata) {
2095             getrusage(RUSAGE_THREAD, &thread);
2096
2097             if(unlikely(!stcpu_thread)) {
2098                 stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
2099                 if(unlikely(!stcpu_thread))
2100                     stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED);
2101
2102                 rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
2103                 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
2104             }
2105             else
2106                 rrdset_next(stcpu_thread);
2107
2108             rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
2109             rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
2110             rrdset_done(stcpu_thread);
2111         }
2112     }
2113
2114     info("CGROUP thread exiting");
2115
2116     static_thread->enabled = 0;
2117     pthread_exit(NULL);
2118     return NULL;
2119 }