X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=src%2Fplugin_tc.c;h=d70f52650b5601b5c52891b6c29df881eee6ab60;hb=d4a462d10af143d17a574e8fe0a1280875e8ca41;hp=8a4e5c74f1ef29d0e61e518a0349a65cba1635f5;hpb=ed44ce7b87de9070f5ecb27a15198807cd0c55d5;p=netdata.git diff --git a/src/plugin_tc.c b/src/plugin_tc.c old mode 100755 new mode 100644 index 8a4e5c74..d70f5265 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -1,15 +1,21 @@ -#include +#ifdef HAVE_CONFIG_H +#include +#endif #include #include #include +#include +#include #include "avl.h" #include "log.h" #include "common.h" -#include "config.h" +#include "appconfig.h" #include "rrd.h" #include "popen.h" #include "plugin_tc.h" +#include "main.h" +#include "../config.h" #define RRD_TYPE_TC "tc" #define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC) @@ -37,6 +43,15 @@ struct tc_class { char hasparent; char isleaf; unsigned long long bytes; + unsigned long long packets; + unsigned long long dropped; + unsigned long long overlimits; + unsigned long long requeues; + unsigned long long lended; + unsigned long long borrowed; + unsigned long long giants; + unsigned long long tokens; + unsigned long long ctokens; char updated; // updated bytes char seen; // seen in the tc list (even without bytes) @@ -57,9 +72,14 @@ struct tc_device { avl_tree classes_index; struct tc_class *classes; + + struct tc_device *next; + struct tc_device *prev; }; +struct tc_device *tc_device_root = NULL; + // ---------------------------------------------------------------------------- // tc_device index @@ -73,7 +93,14 @@ static int tc_device_compare(void* a, void* b) { avl_tree tc_device_root_index = { NULL, - tc_device_compare + tc_device_compare, +#ifndef AVL_WITHOUT_PTHREADS +#ifdef AVL_LOCK_WITH_MUTEX + PTHREAD_MUTEX_INITIALIZER +#else + PTHREAD_RWLOCK_INITIALIZER +#endif +#endif }; #define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st)) @@ -119,7 +146,10 @@ static void tc_class_free(struct tc_device *n, struct tc_class *c) { if(c->next) c->next->prev = c->prev; if(c->prev) c->prev->next = c->next; - if(n->classes == c) n->classes = c->next; + if(n->classes == c) { + if(c->next) n->classes = c->next; + else n->classes = c->prev; + } tc_class_index_del(n, c); @@ -135,7 +165,7 @@ static void tc_device_classes_cleanup(struct tc_device *d) { static int cleanup_every = 999; if(cleanup_every > 0) { - cleanup_every = -config_get_number("plugin:tc", "cleanup unused classes every", 60); + cleanup_every = (int) -config_get_number("plugin:tc", "cleanup unused classes every", 60); if(cleanup_every > 0) cleanup_every = -cleanup_every; if(cleanup_every == 0) cleanup_every = -1; } @@ -161,7 +191,7 @@ static void tc_device_commit(struct tc_device *d) static int enable_new_interfaces = -1; if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1); - + // we only need to add leaf classes struct tc_class *c, *x; @@ -189,7 +219,7 @@ static void tc_device_commit(struct tc_device *d) } } } - + // debugging: /* for ( c = d->classes ; c ; c = c->next) { @@ -217,13 +247,13 @@ static void tc_device_commit(struct tc_device *d) if(!st) { debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id); - st = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, "Class Usage", "kilobits/s", 1000, rrd_update_every, RRDSET_TYPE_STACKED); + st = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED); for(c = d->classes ; c ; c = c->next) { if(!c->updated) continue; if(c->isleaf && c->hasparent) - rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL); + rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); } } else { @@ -237,23 +267,23 @@ static void tc_device_commit(struct tc_device *d) if(!c->updated) continue; if(c->isleaf && c->hasparent) { - if(rrddim_set(st, c->id, c->bytes) != 0) { + RRDDIM *rd = rrddim_find(st, c->id); + + if(!rd) { debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s'", st->id, c->id, c->name); - + // new class, we have to add it - rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL); - rrddim_set(st, c->id, c->bytes); + rd = rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); } else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", st->id, c->id); + rrddim_set_by_pointer(st, rd, c->bytes); + // if it has a name, different to the id if(c->name) { // update the rrd dimension with the new name - RRDDIM *rd = rrddim_find(st, c->id); - if(rd) { - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name); - rrddim_set_name(st, rd, c->name); - } + debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name); + rrddim_set_name(st, rd, c->name); free(c->name); c->name = NULL; @@ -320,7 +350,27 @@ static struct tc_device *tc_device_create(char *id) d->classes_index.root = NULL; d->classes_index.compar = tc_class_compare; + int lock; +#ifndef AVL_WITHOUT_PTHREADS +#ifdef AVL_LOCK_WITH_MUTEX + lock = pthread_mutex_init(&d->classes_index.mutex, NULL); +#else + lock = pthread_rwlock_init(&d->classes_index.rwlock, NULL); +#endif +#endif + if(lock != 0) + fatal("Failed to initialize plugin_tc mutex/rwlock, return code %d.", lock); + tc_device_index_add(d); + + if(!tc_device_root) { + tc_device_root = d; + } + else { + d->next = tc_device_root; + tc_device_root->prev = d; + tc_device_root = d; + } } return(d); @@ -367,9 +417,16 @@ static struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parent return(c); } -/* + static void tc_device_free(struct tc_device *n) { + if(n->next) n->next->prev = n->prev; + if(n->prev) n->prev->next = n->next; + if(tc_device_root == n) { + if(n->next) tc_device_root = n->next; + else tc_device_root = n->prev; + } + tc_device_index_del(n); while(n->classes) tc_class_free(n, n->classes); @@ -380,11 +437,16 @@ static void tc_device_free(struct tc_device *n) free(n); } -*/ + +static void tc_device_free_all() +{ + while(tc_device_root) + tc_device_free(tc_device_root); +} #define MAX_WORDS 20 -int tc_space(char c) { +static inline int tc_space(char c) { switch(c) { case ' ': case '\t': @@ -397,7 +459,7 @@ int tc_space(char c) { } } -void tc_split_words(char *str, char **words, int max_words) { +static void tc_split_words(char *str, char **words, int max_words) { char *s = str; int i = 0; @@ -437,12 +499,17 @@ void *tc_main(void *ptr) { if(ptr) { ; } + info("TC thread created with task id %d", gettid()); + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) error("Cannot set pthread cancel type to DEFERRED."); if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); + struct rusage thread; + RRDSET *stcpu = NULL, *sttime = NULL; + char buffer[TC_LINE_MAX+1] = ""; char *words[MAX_WORDS] = { NULL }; @@ -450,15 +517,20 @@ void *tc_main(void *ptr) uint32_t END_HASH = simple_hash("END"); uint32_t CLASS_HASH = simple_hash("class"); uint32_t SENT_HASH = simple_hash("Sent"); + uint32_t LENDED_HASH = simple_hash("lended:"); + uint32_t TOKENS_HASH = simple_hash("tokens:"); uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME"); uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP"); uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME"); + uint32_t WORKTIME_HASH = simple_hash("WORKTIME"); #ifdef DETACH_PLUGINS_FROM_NETDATA uint32_t MYPID_HASH = simple_hash("MYPID"); #endif uint32_t first_hash; for(;1;) { + if(unlikely(netdata_exit)) break; + FILE *fp; struct tc_device *device = NULL; struct tc_class *class = NULL; @@ -469,10 +541,13 @@ void *tc_main(void *ptr) fp = mypopen(buffer, &tc_child_pid); if(!fp) { error("TC: Cannot popen(\"%s\", \"r\").", buffer); + pthread_exit(NULL); return NULL; } while(fgets(buffer, TC_LINE_MAX, fp) != NULL) { + if(unlikely(netdata_exit)) break; + buffer[TC_LINE_MAX] = '\0'; // debug(D_TC_LOOP, "TC: read '%s'", buffer); @@ -485,11 +560,21 @@ void *tc_main(void *ptr) first_hash = simple_hash(words[0]); - if(first_hash == CLASS_HASH && strcmp(words[0], "class") == 0 && device) { + if(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) { // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]); - if(words[1] && words[2] && words[3] && words[4] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0)) { - // char *type = words[1]; // the class: htb, fq_codel, etc + // clear the last class + class = NULL; + + // words[1] : class type + // words[2] : N:XX + // words[3] : parent or root + if(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0)) { + //char *type = words[1]; // the class: htb, fq_codel, etc + + // we are only interested for HTB classes + //if(strcmp(type, "htb") != 0) continue; + char *id = words[2]; // the class major:minor char *parent = words[3]; // 'parent' or 'root' char *parentid = words[4]; // the parent's id @@ -543,28 +628,85 @@ void *tc_main(void *ptr) class = NULL; } } - else if(first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0 && device && class) { + else if(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0) { // debug(D_TC_LOOP, "SENT line '%s'", words[1]); if(words[1] && *words[1]) { - class->bytes = atoll(words[1]); + class->bytes = strtoull(words[1], NULL, 10); class->updated = 1; } - else class->bytes = 0; + + if(words[3] && *words[3]) + class->packets = strtoull(words[3], NULL, 10); + + if(words[6] && *words[6]) + class->dropped = strtoull(words[6], NULL, 10); + + if(words[8] && *words[8]) + class->overlimits = strtoull(words[8], NULL, 10); + + if(words[10] && *words[10]) + class->requeues = strtoull(words[8], NULL, 10); + } + else if(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0) { + // debug(D_TC_LOOP, "LENDED line '%s'", words[1]); + if(words[1] && *words[1]) + class->lended = strtoull(words[1], NULL, 10); + + if(words[3] && *words[3]) + class->borrowed = strtoull(words[3], NULL, 10); + + if(words[5] && *words[5]) + class->giants = strtoull(words[5], NULL, 10); } - else if(first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0 && device) { + else if(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0) { + // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]); + if(words[1] && *words[1]) + class->tokens = strtoull(words[1], NULL, 10); + + if(words[3] && *words[3]) + class->ctokens = strtoull(words[3], NULL, 10); + } + else if(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0) { // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]); if(words[1] && *words[1]) tc_device_set_device_name(device, words[1]); } - else if(first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0 && device) { + else if(device && first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0) { // debug(D_TC_LOOP, "SETDEVICEGROUP line '%s'", words[1]); if(words[1] && *words[1]) tc_device_set_device_family(device, words[1]); } - else if(first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0 && device) { + else if(device && first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0) { // debug(D_TC_LOOP, "SETCLASSNAME line '%s' '%s'", words[1], words[2]); char *id = words[1]; char *path = words[2]; if(id && *id && path && *path) tc_device_set_class_name(device, id, path); } + else if(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0) { + // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]); + getrusage(RUSAGE_THREAD, &thread); + + if(!stcpu) stcpu = rrdset_find("netdata.plugin_tc_cpu"); + if(!stcpu) { + stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED); + rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); + rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + } + else rrdset_next(stcpu); + + rrddim_set(stcpu, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); + rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); + rrdset_done(stcpu); + + if(!sttime) stcpu = rrdset_find("netdata.plugin_tc_time"); + if(!sttime) { + sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(sttime, "run_time", "run time", 1, 1, RRDDIM_ABSOLUTE); + } + else rrdset_next(sttime); + + rrddim_set(sttime, "run_time", atoll(words[1])); + rrdset_done(sttime); + + } #ifdef DETACH_PLUGINS_FROM_NETDATA else if(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0)) { // debug(D_TC_LOOP, "MYPID line '%s'", words[1]); @@ -580,7 +722,9 @@ void *tc_main(void *ptr) // debug(D_TC_LOOP, "IGNORED line"); //} } - mypclose(fp); + // fgets() failed or loop broke + int code = mypclose(fp, tc_child_pid); + tc_child_pid = 0; if(device) { // tc_device_free(device); @@ -588,9 +732,25 @@ void *tc_main(void *ptr) class = NULL; } - sleep(rrd_update_every); + if(netdata_exit) { + tc_device_free_all(); + pthread_exit(NULL); + return NULL; + } + + if(code == 1 || code == 127) { + // 1 = DISABLE + // 127 = cannot even run it + error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code); + + tc_device_free_all(); + pthread_exit(NULL); + return NULL; + } + + sleep((unsigned int) rrd_update_every); } + pthread_exit(NULL); return NULL; } -