]> arthur.barton.de Git - netdata.git/blobdiff - src/plugin_tc.c
improved netdata cleanup/exit procedure
[netdata.git] / src / plugin_tc.c
index 7d521818ed50133be8ab96e766375c9f6375c7c0..d5fd86aac77ddb4b5ffc4800595e60e633c069de 100644 (file)
@@ -39,6 +39,8 @@ struct tc_class {
     RRDDIM *rd_bytes;
     RRDDIM *rd_packets;
     RRDDIM *rd_dropped;
+    RRDDIM *rd_tokens;
+    RRDDIM *rd_ctokens;
 
     char name_updated;
     char updated;   // updated bytes
@@ -64,10 +66,14 @@ struct tc_device {
     char enabled_bytes;
     char enabled_packets;
     char enabled_dropped;
+    char enabled_tokens;
+    char enabled_ctokens;
 
     RRDSET *st_bytes;
     RRDSET *st_packets;
     RRDSET *st_dropped;
+    RRDSET *st_tokens;
+    RRDSET *st_ctokens;
 
     avl_tree classes_index;
 
@@ -94,8 +100,8 @@ avl_tree tc_device_root_index = {
         tc_device_compare
 };
 
-#define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st))
-#define tc_device_index_del(st) avl_remove(&tc_device_root_index, (avl *)(st))
+#define tc_device_index_add(st) (struct tc_device *)avl_insert(&tc_device_root_index, (avl *)(st))
+#define tc_device_index_del(st) (struct tc_device *)avl_remove(&tc_device_root_index, (avl *)(st))
 
 static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) {
     struct tc_device tmp;
@@ -115,8 +121,8 @@ static int tc_class_compare(void* a, void* b) {
     else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
 }
 
-#define tc_class_index_add(st, rd) avl_insert(&((st)->classes_index), (avl *)(rd))
-#define tc_class_index_del(st, rd) avl_remove(&((st)->classes_index), (avl *)(rd))
+#define tc_class_index_add(st, rd) (struct tc_class *)avl_insert(&((st)->classes_index), (avl *)(rd))
+#define tc_class_index_del(st, rd) (struct tc_class *)avl_remove(&((st)->classes_index), (avl *)(rd))
 
 static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) {
     struct tc_class tmp;
@@ -129,16 +135,19 @@ static inline struct tc_class *tc_class_index_find(struct tc_device *st, const c
 // ----------------------------------------------------------------------------
 
 static inline void tc_class_free(struct tc_device *n, struct tc_class *c) {
-    debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
-
+    if(c == n->classes) {
+        if(c->next)
+            n->classes = c->next;
+        else
+            n->classes = c->prev;
+    }
     if(c->next) c->next->prev = c->prev;
     if(c->prev) c->prev->next = c->next;
-    if(n->classes == c) {
-        if(c->next) n->classes = c->next;
-        else n->classes = c->prev;
-    }
 
-    tc_class_index_del(n, c);
+    debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
+
+    if(unlikely(tc_class_index_del(n, c) != c))
+        error("plugin_tc: INTERNAL ERROR: attempt remove class '%s' from device '%s': removed a different calls", c->id, n->id);
 
     freez(c->id);
     freez(c->name);
@@ -175,18 +184,20 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) {
 }
 
 static inline void tc_device_commit(struct tc_device *d) {
-    static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1;
+    static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1;
 
     if(unlikely(enable_new_interfaces == -1)) {
         enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
         enable_bytes          = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
         enable_packets        = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
         enable_dropped        = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+        enable_tokens         = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_ONDEMAND_NO);
+        enable_ctokens        = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_ONDEMAND_NO);
     }
 
     // we only need to add leaf classes
     struct tc_class *c, *x;
-    unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0;
+    unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0;
     int active_classes = 0;
 
     // set all classes
@@ -214,23 +225,26 @@ static inline void tc_device_commit(struct tc_device *d) {
         }
     }
 
-    // debugging:
+    // debugging only
     /*
-    for ( c = d->classes ; c ; c = c->next) {
-        if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
-        else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
+    if(unlikely(debug_flags & D_TC_LOOP)) {
+        for(c = d->classes ; c ; c = c->next) {
+            if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device '%s', class %s, OK", d->name, c->id);
+            else debug(D_TC_LOOP, "TC: Device '%s', class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)");
+        }
     }
     */
 
     // we need at least a class
     for(c = d->classes ; c ; c = c->next) {
         // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
-        if(!c->updated) continue;
-        if(c->isleaf && c->hasparent) {
+        if(unlikely(c->updated && c->isleaf && c->hasparent)) {
             active_classes++;
             bytes_sum += c->bytes;
             packets_sum += c->packets;
             dropped_sum += c->dropped;
+            tokens_sum += c->tokens;
+            ctokens_sum += c->ctokens;
         }
     }
 
@@ -240,7 +254,7 @@ static inline void tc_device_commit(struct tc_device *d) {
         return;
     }
 
-    if(unlikely(d->enabled == -1)) {
+    if(unlikely(d->enabled == (char)-1)) {
         char var_name[CONFIG_MAX_NAME + 1];
         snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
         d->enabled         = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
@@ -253,8 +267,30 @@ static inline void tc_device_commit(struct tc_device *d) {
 
         snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
         d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
+
+        snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id);
+        d->enabled_tokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens);
+
+        snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id);
+        d->enabled_ctokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens);
     }
 
+    debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d), classes = %d (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).",
+        d->name?d->name:d->id,
+        d->enabled, enable_new_interfaces,
+        d->enabled_bytes, enable_bytes,
+        d->enabled_packets, enable_packets,
+        d->enabled_dropped, enable_dropped,
+        d->enabled_tokens, enable_tokens,
+        d->enabled_ctokens, enable_ctokens,
+        active_classes,
+        bytes_sum,
+        packets_sum,
+        dropped_sum,
+        tokens_sum,
+        ctokens_sum
+        );
+
     if(likely(d->enabled)) {
         // --------------------------------------------------------------------
         // bytes
@@ -271,7 +307,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             }
             else {
                 debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id);
-                rrdset_next_plugins(d->st_bytes);
+                rrdset_next(d->st_bytes);
 
                 if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) {
                     rrdset_set_name(d->st_bytes, d->name);
@@ -332,7 +368,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             }
             else {
                 debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id);
-                rrdset_next_plugins(d->st_packets);
+                rrdset_next(d->st_packets);
 
                 // FIXME
                 // update the family
@@ -386,7 +422,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             }
             else {
                 debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id);
-                rrdset_next_plugins(d->st_dropped);
+                rrdset_next(d->st_dropped);
 
                 // FIXME
                 // update the family
@@ -419,6 +455,114 @@ static inline void tc_device_commit(struct tc_device *d) {
             }
             rrdset_done(d->st_dropped);
         }
+
+        // --------------------------------------------------------------------
+        // tokens
+        
+        if(d->enabled_tokens == CONFIG_ONDEMAND_YES || (d->enabled_tokens == CONFIG_ONDEMAND_ONDEMAND && tokens_sum)) {
+            d->enabled_tokens = CONFIG_ONDEMAND_YES;
+            
+            if(unlikely(!d->st_tokens)) {
+                char id[RRD_ID_LENGTH_MAX + 1];
+                char name[RRD_ID_LENGTH_MAX + 1];
+                snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id);
+                snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id);
+
+                d->st_tokens = rrdset_find_bytype(RRD_TYPE_TC, id);
+                if(unlikely(!d->st_tokens)) {
+                    debug(D_TC_LOOP, "TC: Creating new _tokens chart for device '%s'", d->name?d->name:d->id);
+                    d->st_tokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030, rrd_update_every, RRDSET_TYPE_LINE);
+                }
+            }
+            else {
+                debug(D_TC_LOOP, "TC: Updating _tokens chart for device '%s'", d->name?d->name:d->id);
+                rrdset_next(d->st_tokens);
+
+                // FIXME
+                // update the family
+            }
+
+            for(c = d->classes ; c ; c = c->next) {
+                if(unlikely(!c->updated)) continue;
+
+                if(c->isleaf && c->hasparent) {
+                    if(unlikely(!c->rd_tokens)) {
+                        c->rd_tokens = rrddim_find(d->st_tokens, c->id);
+                        if(unlikely(!c->rd_tokens)) {
+                            debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_tokens->id, c->id, c->name);
+
+                            // new class, we have to add it
+                            c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+                        }
+                        else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_tokens->id, c->id);
+                    }
+
+                    rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens);
+
+                    // if it has a name, different to the id
+                    if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+                        // update the rrd dimension with the new name
+                        debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_tokens->id, c->rd_tokens->id, c->name);
+                        rrddim_set_name(d->st_tokens, c->rd_tokens, c->name);
+                    }
+                }
+            }
+            rrdset_done(d->st_tokens);
+        }
+
+        // --------------------------------------------------------------------
+        // ctokens
+        
+        if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) {
+            d->enabled_ctokens = CONFIG_ONDEMAND_YES;
+            
+            if(unlikely(!d->st_ctokens)) {
+                char id[RRD_ID_LENGTH_MAX + 1];
+                char name[RRD_ID_LENGTH_MAX + 1];
+                snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id);
+                snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id);
+
+                d->st_ctokens = rrdset_find_bytype(RRD_TYPE_TC, id);
+                if(unlikely(!d->st_ctokens)) {
+                    debug(D_TC_LOOP, "TC: Creating new _ctokens chart for device '%s'", d->name?d->name:d->id);
+                    d->st_ctokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040, rrd_update_every, RRDSET_TYPE_LINE);
+                }
+            }
+            else {
+                debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id);
+                rrdset_next(d->st_ctokens);
+
+                // FIXME
+                // update the family
+            }
+
+            for(c = d->classes ; c ; c = c->next) {
+                if(unlikely(!c->updated)) continue;
+
+                if(c->isleaf && c->hasparent) {
+                    if(unlikely(!c->rd_ctokens)) {
+                        c->rd_ctokens = rrddim_find(d->st_ctokens, c->id);
+                        if(unlikely(!c->rd_ctokens)) {
+                            debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_ctokens->id, c->id, c->name);
+
+                            // new class, we have to add it
+                            c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+                        }
+                        else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_ctokens->id, c->id);
+                    }
+
+                    rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens);
+
+                    // if it has a name, different to the id
+                    if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+                        // update the rrd dimension with the new name
+                        debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_ctokens->id, c->rd_ctokens->id, c->name);
+                        rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name);
+                    }
+                }
+            }
+            rrdset_done(d->st_ctokens);
+        }
     }
 
     tc_device_classes_cleanup(d);
@@ -473,10 +617,11 @@ static inline struct tc_device *tc_device_create(char *id)
 
         d->id = strdupz(id);
         d->hash = simple_hash(d->id);
-        d->enabled = -1;
+        d->enabled = (char)-1;
 
         avl_init(&d->classes_index, tc_class_compare);
-        tc_device_index_add(d);
+        if(unlikely(tc_device_index_add(d) != d))
+            error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", d->id);
 
         if(!tc_device_root) {
             tc_device_root = d;
@@ -517,7 +662,8 @@ static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char
             c->leaf_hash = simple_hash(c->leafid);
         }
 
-        tc_class_index_add(n, c);
+        if(unlikely(tc_class_index_add(n, c) != c))
+            error("plugin_tc: INTERNAL ERROR: attempt index class '%s' on device '%s': already exists", c->id, n->id);
     }
 
     c->seen = 1;
@@ -534,7 +680,8 @@ static inline void tc_device_free(struct tc_device *n)
         else tc_device_root = n->prev;
     }
 
-    tc_device_index_del(n);
+    if(unlikely(tc_device_index_del(n) != n))
+        error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", n->id);
 
     while(n->classes) tc_class_free(n, n->classes);
 
@@ -602,7 +749,7 @@ static inline void tc_split_words(char *str, char **words, int max_words) {
 
 pid_t tc_child_pid = 0;
 void *tc_main(void *ptr) {
-    (void)ptr;
+    struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
 
     info("TC thread created with task id %d", gettid());
 
@@ -649,8 +796,7 @@ void *tc_main(void *ptr) {
         fp = mypopen(buffer, &tc_child_pid);
         if(unlikely(!fp)) {
             error("TC: Cannot popen(\"%s\", \"r\").", buffer);
-            pthread_exit(NULL);
-            return NULL;
+            goto cleanup;
         }
 
         while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
@@ -851,8 +997,7 @@ void *tc_main(void *ptr) {
 
         if(unlikely(netdata_exit)) {
             tc_device_free_all();
-            pthread_exit(NULL);
-            return NULL;
+            goto cleanup;
         }
 
         if(code == 1 || code == 127) {
@@ -861,13 +1006,17 @@ void *tc_main(void *ptr) {
             error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
 
             tc_device_free_all();
-            pthread_exit(NULL);
-            return NULL;
+            goto cleanup;
         }
 
         sleep((unsigned int) rrd_update_every);
     }
 
+cleanup:
+    info("TC thread exiting");
+
+    static_thread->enabled = 0;
+    static_thread->thread = NULL;
     pthread_exit(NULL);
     return NULL;
 }