]> arthur.barton.de Git - netdata.git/blobdiff - src/sys_fs_cgroup.c
added diskspace plugin monitoring charts
[netdata.git] / src / sys_fs_cgroup.c
index 14c391776294e87434f46f2b79b9f41eeb30b058..5ee9d2356377978379533f77994c658a87d2e018 100644 (file)
@@ -28,7 +28,7 @@ void read_cgroup_plugin_configuration() {
     cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
 
     char filename[FILENAME_MAX + 1], *s;
-    struct mountinfo *mi, *root = mountinfo_read();
+    struct mountinfo *mi, *root = mountinfo_read(0);
 
     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
@@ -138,6 +138,18 @@ struct memory {
     unsigned long long total_active_file;
     unsigned long long total_unevictable;
 */
+
+    int usage_in_bytes_updated;
+    char *filename_usage_in_bytes;
+    unsigned long long usage_in_bytes;
+
+    int msw_usage_in_bytes_updated;
+    char *filename_msw_usage_in_bytes;
+    unsigned long long msw_usage_in_bytes;
+
+    int failcnt_updated;
+    char *filename_failcnt;
+    unsigned long long failcnt;
 };
 
 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
@@ -160,9 +172,13 @@ struct cpuacct_usage {
     unsigned long long *cpu_percpu;
 };
 
+#define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001
+
 struct cgroup {
-    int available;      // found in the filesystem
-    int enabled;        // enabled in the config
+    uint32_t options;
+
+    char available;      // found in the filesystem
+    char enabled;        // enabled in the config
 
     char *id;
     uint32_t hash;
@@ -253,7 +269,7 @@ void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
         }
 
         unsigned long i = procfile_linewords(ff, 0);
-        if(i <= 0) return;
+        if(i == 0) return;
 
         // we may have 1 more CPU reported
         while(i > 0) {
@@ -553,6 +569,24 @@ void cgroup_read_memory(struct memory *mem) {
 
         mem->updated = 1;
     }
+
+    mem->usage_in_bytes_updated = 0;
+    if(mem->filename_usage_in_bytes) {
+        if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes)))
+            mem->usage_in_bytes_updated = 1;
+    }
+
+    mem->msw_usage_in_bytes_updated = 0;
+    if(mem->filename_msw_usage_in_bytes) {
+        if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes)))
+            mem->msw_usage_in_bytes_updated = 1;
+    }
+
+    mem->failcnt_updated = 0;
+    if(mem->filename_failcnt) {
+        if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt)))
+            mem->failcnt_updated = 1;
+    }
 }
 
 void cgroup_read(struct cgroup *cg) {
@@ -639,7 +673,6 @@ struct cgroup *cgroup_add(const char *id) {
 
         // disable by default the root cgroup
         def = 0;
-        debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled");
     }
     else {
         if(*chart_id == '/') chart_id++;
@@ -660,7 +693,7 @@ struct cgroup *cgroup_add(const char *id) {
                 !strcmp(chart_id, "system") ||
                 !strcmp(chart_id, "machine") ||
                 // starts with them
-                (len >  6 && !strncmp(chart_id, "user/", 6)) ||
+                (len >  5 && !strncmp(chart_id, "user/", 5)) ||
                 (len > 11 && !strncmp(chart_id, "user.slice/", 11)) ||
                 // ends with them
                 (len >  5 && !strncmp(&chart_id[len -  5], ".user", 5)) ||
@@ -669,12 +702,14 @@ struct cgroup *cgroup_add(const char *id) {
                 (len >  6 && !strncmp(&chart_id[len -  6], ".mount", 6)) ||
                 (len >  8 && !strncmp(&chart_id[len -  8], ".session", 8)) ||
                 (len >  8 && !strncmp(&chart_id[len -  8], ".service", 8)) ||
-                (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10))
+                (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10)) ||
+                // starts and ends with them
+                (len > 7 && !strncmp(chart_id, "lxc/", 4) && !strncmp(&chart_id[len - 3], "/ns", 3)) // #1397
                 ) {
             def = 0;
-            debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled");
         }
     }
+    debug(D_CGROUP, "cgroup '%s' (chart_id '%s') is (by default) %s", id, chart_id, (def)?"enabled":"disabled");
 
     struct cgroup *cg = callocz(1, sizeof(struct cgroup));
 
@@ -714,11 +749,18 @@ struct cgroup *cgroup_add(const char *id) {
                 if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
                     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'.",
                           cg->chart_id, t->id, cg->id, t->id);
+                    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'.",
+                          cg->chart_id, t->id, cg->id, t->id);
                     t->enabled = 0;
-                } else {
-                    error("Control group with chart id '%s' already exists with id '%s' and is enabled. Disabling cgroup with id '%s'.",
+                    t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
+                }
+                else {
+                    error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
+                          cg->chart_id, t->id, cg->id);
+                    debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
                           cg->chart_id, t->id, cg->id);
                     cg->enabled = 0;
+                    cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
                 }
 
                 break;
@@ -802,8 +844,8 @@ void found_subdir_in_dir(const char *dir) {
 int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
     debug(D_CGROUP, "searching for directories in '%s'", base);
 
-    int ret = 0;
-    int enabled = 0;
+    int ret = -1;
+    int enabled = -1;
     if(!this) this = base;
     size_t dirlen = strlen(this), baselen = strlen(base);
     const char *relative_path = &this[baselen];
@@ -834,13 +876,20 @@ int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(con
                 if(*r == '\0') r = "/";
                 else if (*r == '/') r++;
 
+                // do not decent in directories we are not interested
+                // https://github.com/firehol/netdata/issues/345
+                int def = 1;
+                size_t len = strlen(r);
+                if(len >  5 && !strncmp(&r[len -  5], "-qemu", 5))
+                    def = 0;
+
                 // we check for this option here
                 // so that the config will not have settings
                 // for leaf directories
                 char option[FILENAME_MAX + 1];
                 snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
                 option[FILENAME_MAX] = '\0';
-                enabled = config_get_boolean("plugin:cgroups", option, 1);
+                enabled = config_get_boolean("plugin:cgroups", option, def);
             }
 
             if(enabled) {
@@ -865,8 +914,9 @@ void mark_all_cgroups_as_not_available() {
     struct cgroup *cg;
 
     // mark all as not available
-    for(cg = cgroup_root; cg ; cg = cg->next)
+    for(cg = cgroup_root; cg ; cg = cg->next) {
         cg->available = 0;
+    }
 }
 
 void cleanup_all_cgroups() {
@@ -874,6 +924,18 @@ void cleanup_all_cgroups() {
 
     for(; cg ;) {
         if(!cg->available) {
+            // enable the first duplicate cgroup
+            {
+                struct cgroup *t;
+                for(t = cgroup_root; t ; t = t->next) {
+                    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)) {
+                        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);
+                        t->enabled = 1;
+                        t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
+                        break;
+                    }
+                }
+            }
 
             if(!last)
                 cgroup_root = cg->next;
@@ -943,7 +1005,7 @@ void find_all_cgroups() {
         // check for newly added cgroups
         // and update the filenames they read
         char filename[FILENAME_MAX + 1];
-        if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
+        if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
             if(stat(filename, &buf) != -1) {
                 cg->cpuacct_stat.filename = strdupz(filename);
@@ -951,7 +1013,8 @@ void find_all_cgroups() {
             }
             else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
         }
-        if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
+
+        if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename)) {
             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
             if(stat(filename, &buf) != -1) {
                 cg->cpuacct_usage.filename = strdupz(filename);
@@ -959,16 +1022,48 @@ void find_all_cgroups() {
             }
             else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
         }
-        if(cgroup_enable_memory && !cg->memory.filename) {
-            snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
-            if(stat(filename, &buf) != -1) {
-                cg->memory.filename = strdupz(filename);
-                debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
+
+        if(unlikely(cgroup_enable_memory)) {
+            if(unlikely(!cg->memory.filename)) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->memory.filename = strdupz(filename);
+                    debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
+                }
+                else
+                    debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(unlikely(!cg->memory.filename_usage_in_bytes)) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->memory.filename_usage_in_bytes = strdupz(filename);
+                    debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
+                }
+                else
+                    debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(unlikely(!cg->memory.filename_msw_usage_in_bytes)) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
+                    debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
+                }
+                else
+                    debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(unlikely(!cg->memory.filename_failcnt)) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->memory.filename_failcnt = strdupz(filename);
+                    debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
+                }
+                else
+                    debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
         }
-        if(cgroup_enable_blkio) {
-            if(!cg->io_service_bytes.filename) {
+
+        if(unlikely(cgroup_enable_blkio)) {
+            if(unlikely(!cg->io_service_bytes.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->io_service_bytes.filename = strdupz(filename);
@@ -976,7 +1071,7 @@ void find_all_cgroups() {
                 }
                 else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            if(!cg->io_serviced.filename) {
+            if(unlikely(!cg->io_serviced.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->io_serviced.filename = strdupz(filename);
@@ -984,7 +1079,7 @@ void find_all_cgroups() {
                 }
                 else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            if(!cg->throttle_io_service_bytes.filename) {
+            if(unlikely(!cg->throttle_io_service_bytes.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->throttle_io_service_bytes.filename = strdupz(filename);
@@ -992,7 +1087,7 @@ void find_all_cgroups() {
                 }
                 else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            if(!cg->throttle_io_serviced.filename) {
+            if(unlikely(!cg->throttle_io_serviced.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->throttle_io_serviced.filename = strdupz(filename);
@@ -1000,7 +1095,7 @@ void find_all_cgroups() {
                 }
                 else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            if(!cg->io_merged.filename) {
+            if(unlikely(!cg->io_merged.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->io_merged.filename = strdupz(filename);
@@ -1008,7 +1103,7 @@ void find_all_cgroups() {
                 }
                 else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
             }
-            if(!cg->io_queued.filename) {
+            if(unlikely(!cg->io_queued.filename)) {
                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
                 if(stat(filename, &buf) != -1) {
                     cg->io_queued.filename = strdupz(filename);
@@ -1053,7 +1148,7 @@ void update_cgroup_charts(int update_every) {
         if(cg->cpuacct_stat.updated) {
             st = rrdset_find_bytype(type, "cpu");
             if(!st) {
-                snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
+                snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors>1)?"s":"", cg->chart_title);
                 st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
 
                 rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
@@ -1072,7 +1167,7 @@ void update_cgroup_charts(int update_every) {
 
             st = rrdset_find_bytype(type, "cpu_per_core");
             if(!st) {
-                snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
+                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);
                 st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
 
                 for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
@@ -1094,7 +1189,7 @@ void update_cgroup_charts(int update_every) {
                 st = rrdset_find_bytype(type, "mem");
                 if(!st) {
                     snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
-                    st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
+                    st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every,
                                        RRDSET_TYPE_STACKED);
 
                     rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
@@ -1167,12 +1262,44 @@ void update_cgroup_charts(int update_every) {
             }
         }
 
+        if(cg->memory.usage_in_bytes_updated) {
+            st = rrdset_find_bytype(type, "mem_usage");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200,
+                                   update_every, RRDSET_TYPE_STACKED);
+
+                rrddim_add(st, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "ram", cg->memory.usage_in_bytes);
+            rrddim_set(st, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0);
+            rrdset_done(st);
+        }
+
+        if(cg->memory.failcnt_updated && cg->memory.failcnt > 0) {
+            st = rrdset_find_bytype(type, "mem_failcnt");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "MB", 40250,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "failures", cg->memory.failcnt);
+            rrdset_done(st);
+        }
+
         if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
             st = rrdset_find_bytype(type, "io");
             if(!st) {
                 snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
                 st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
-                                   update_every, RRDSET_TYPE_LINE);
+                                   update_every, RRDSET_TYPE_AREA);
 
                 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
                 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
@@ -1202,11 +1329,11 @@ void update_cgroup_charts(int update_every) {
         }
 
         if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
-            st = rrdset_find_bytype(type, "io");
+            st = rrdset_find_bytype(type, "throttle_io");
             if(!st) {
                 snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
-                st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
-                                   update_every, RRDSET_TYPE_LINE);
+                st = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", 41200,
+                                   update_every, RRDSET_TYPE_AREA);
 
                 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
                 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
@@ -1277,12 +1404,12 @@ void update_cgroup_charts(int update_every) {
 // ----------------------------------------------------------------------------
 // cgroups main
 
-int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
+int do_sys_fs_cgroup(int update_every, usec_t dt) {
     (void)dt;
 
     static int cgroup_global_config_read = 0;
     static time_t last_run = 0;
-    time_t now = time(NULL);
+    time_t now = now_realtime_sec();
 
     if(unlikely(!cgroup_global_config_read)) {
         read_cgroup_plugin_configuration();
@@ -1300,9 +1427,8 @@ int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
     return 0;
 }
 
-void *cgroups_main(void *ptr)
-{
-    if(ptr) { ; }
+void *cgroups_main(void *ptr) {
+    struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
 
     info("CGROUP Plugin thread created with task id %d", gettid());
 
@@ -1319,24 +1445,24 @@ void *cgroups_main(void *ptr)
     int vdo_cpu_netdata             = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
 
     // keep track of the time each module was called
-    unsigned long long sutime_sys_fs_cgroup = 0ULL;
+    usec_t sutime_sys_fs_cgroup = 0ULL;
 
     // the next time we will run - aligned properly
-    unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
-    unsigned long long sunow;
+    usec_t sunext = (now_realtime_sec() - (now_realtime_sec() % rrd_update_every) + rrd_update_every) * USEC_PER_SEC;
 
     RRDSET *stcpu_thread = NULL;
 
-    for(;1;) {
+    for(;;) {
+        usec_t sunow;
         if(unlikely(netdata_exit)) break;
 
         // delay until it is our time to run
-        while((sunow = time_usec()) < sunext)
+        while((sunow = now_realtime_usec()) < sunext)
             sleep_usec(sunext - sunow);
 
         // find the next time we need to run
-        while(time_usec() > sunext)
-            sunext += rrd_update_every * 1000000ULL;
+        while(now_realtime_usec() > sunext)
+            sunext += rrd_update_every * USEC_PER_SEC;
 
         if(unlikely(netdata_exit)) break;
 
@@ -1344,7 +1470,7 @@ void *cgroups_main(void *ptr)
 
         if(!vdo_sys_fs_cgroup) {
             debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
-            sunow = time_usec();
+            sunow = now_realtime_usec();
             vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
             sutime_sys_fs_cgroup = sunow;
         }
@@ -1359,7 +1485,7 @@ void *cgroups_main(void *ptr)
 
             if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
             if(!stcpu_thread) {
-                stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
+                stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
 
                 rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
                 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
@@ -1372,6 +1498,10 @@ void *cgroups_main(void *ptr)
         }
     }
 
+    info("CGROUP thread exiting");
+
+    static_thread->enabled = 0;
+    static_thread->thread = NULL;
     pthread_exit(NULL);
     return NULL;
 }