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