X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fplugin_tc.c;h=7aef4ab5bc637337068464d66eba4f2cc5cc7473;hb=19c7ffc053db632f4e0fd62e44d323f018895921;hp=bc89de621905bc5fe91ca41a534973e64bfba578;hpb=d734c9572eb95aff3029609d84a840272bc601cc;p=netdata.git diff --git a/src/plugin_tc.c b/src/plugin_tc.c index bc89de62..7aef4ab5 100644 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -1,7 +1,6 @@ #include "common.h" -#define RRD_TYPE_TC "tc" -#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC) +#define RRD_TYPE_TC "tc" // ---------------------------------------------------------------------------- // /sbin/tc processor @@ -26,6 +25,8 @@ struct tc_class { char hasparent; char isleaf; char isqdisc; + char render; + unsigned long long bytes; unsigned long long packets; unsigned long long dropped; @@ -198,98 +199,156 @@ static inline void tc_device_commit(struct tc_device *d) { enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_ONDEMAND_NO); } + 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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); + + snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); + d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); + + snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); + d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); + + snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); + d->enabled_dropped = (char)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 = (char)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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + + snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); + d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); + } + // we only need to add leaf classes - struct tc_class *c, *x; + struct tc_class *c, *x /*, *root = NULL */; unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0; - int active_qos = 0; + int active_nodes = 0, updated_classes = 0, updated_qdiscs = 0; + + // prepare all classes + // we set reasonable defaults for the rest of the code below - // set all classes for(c = d->classes ; c ; c = c->next) { - c->isleaf = 1; - c->hasparent = 0; + c->render = 0; // do not render this class + + c->isleaf = 1; // this is a leaf class + c->hasparent = 0; // without a parent if(unlikely(!c->updated)) - c->unupdated++; - else - c->unupdated = 0; + c->unupdated++; // increase its unupdated counter + else { + c->unupdated = 0; // reset its unupdated counter + + // count how many of each kind + if(c->isqdisc) + updated_qdiscs++; + else + updated_classes++; + } + } + + if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id); + tc_device_classes_cleanup(d); + return; + } + + if(unlikely(updated_classes && updated_qdiscs)) { + error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs); + + // set all classes to !updated + for(c = d->classes ; c ; c = c->next) + if(unlikely(!c->isqdisc && c->updated)) + c->updated = 0; + + updated_classes = 0; } // mark the classes as leafs and parents - for(c = d->classes ; c ; c = c->next) { + // + // TC is hierarchical: + // - classes can have other classes in them + // - the same is true for qdiscs (i.e. qdiscs have classes, that have other qdiscs) + // + // we need to present a chart with leaf nodes only, so that the sum + // of all dimensions of the chart, will be the total utilization + // of the interface. + // + // here we try to find the ones we need to report + // by default all nodes are marked with: isleaf = 1 (see above) + // + // so, here we remove the isleaf flag from nodes in the middle + // and we add the hasparent flag to leaf nodes we found their parent + for(c = d->classes; c; c = c->next) { if(unlikely(!c->updated)) continue; - if(unlikely(c->isqdisc)) continue; - for(x = d->classes ; x ; x = x->next) { - if(unlikely(!x->updated)) continue; - if(unlikely(c == x)) continue; - if(unlikely(x->isqdisc)) continue; + //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.", + // d->id, + // c->isqdisc?"qdisc":"class", + // c->id, + // c->leafid?c->leafid:"NULL", + // c->parentid?c->parentid:"NULL"); + + // find if c is leaf or not + for(x = d->classes; x; x = x->next) { + if(unlikely(!x->updated || c == x || !x->parentid)) continue; - if(x->parentid && ( - ( c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || - (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) { - // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id); + // classes have both parentid and leafid + // qdiscs have only parentid + // the following works for both (it is an OR) + + if( (c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || + (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0)) { + // debug(D_TC_LOOP, "TC: In device '%s', %s '%s' (leafid: '%s') has as leaf %s '%s' (parentid: '%s').", d->name?d->name:d->id, c->isqdisc?"qdisc":"class", c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->isqdisc?"qdisc":"class", x->name?x->name:x->id, x->parentid?x->parentid:x->id); c->isleaf = 0; x->hasparent = 1; } } } - // debugging only - /* - if(unlikely(debug_flags & D_TC_LOOP)) { - for(c = d->classes ; c ; c = c->next) { - if((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs) 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)"); + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->updated)) continue; + + // debug(D_TC_LOOP, "TC: device '%s', %s '%s' isleaf=%d, hasparent=%d", d->id, (c->isqdisc)?"qdisc":"class", c->id, c->isleaf, c->hasparent); + + if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) { + c->render = 1; + active_nodes++; + bytes_sum += c->bytes; + packets_sum += c->packets; + dropped_sum += c->dropped; + tokens_sum += c->tokens; + ctokens_sum += c->ctokens; } + + //if(unlikely(!c->hasparent)) { + // if(root) error("TC: multiple root class/qdisc for device '%s' (old: '%s', new: '%s')", d->id, root->id, c->id); + // root = c; + // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id); + //} } - */ - // we need the root qdisc - 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); - debug(D_TC_LOOP, "TC: Device '%s', class '%s', isleaf=%s, isqdisc=%s, hasparent=%s bytes=%d, packtes=%d, dropped=%d, tokens=%d, ctokens=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf?"true":"false", c->isqdisc?"true":"false", c->hasparent?"true":"false", c->bytes, c->packets, c->dropped, c->tokens, c->ctokens); - if(unlikely(c->updated && c->isqdisc && !c->parentid)) { - active_qos = 1; - bytes_sum = c->bytes; - packets_sum = c->packets; - dropped_sum = c->dropped; - tokens_sum = c->tokens; - ctokens_sum = c->ctokens; - debug(D_TC_LOOP, "TC: found root qdisc. active_qos=%d, bytes_sum=%llu", active_qos, bytes_sum); - break; +#ifdef NETDATA_INTERNAL_CHECKS + // dump all the list to see what we know + + if(unlikely(debug_flags & D_TC_LOOP)) { + for(c = d->classes ; c ; c = c->next) { + if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id); + else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->updated, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); } } +#endif - if(unlikely(!active_qos)) { - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id); + if(unlikely(!active_nodes)) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id); tc_device_classes_cleanup(d); return; } - 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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); - - snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); - d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); - - snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); - d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); - - snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); - d->enabled_dropped = (char)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 = (char)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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); - - snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); - d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); - } - 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, all_classes_qdiscs: %d/%d), classes: (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", d->name?d->name:d->id, d->enabled, enable_new_interfaces, @@ -306,287 +365,211 @@ static inline void tc_device_commit(struct tc_device *d) { ctokens_sum ); - if(likely(d->enabled)) { - // -------------------------------------------------------------------- - // bytes + // -------------------------------------------------------------------- + // bytes - if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) { - d->enabled_bytes = CONFIG_ONDEMAND_YES; + if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) { + d->enabled_bytes = CONFIG_ONDEMAND_YES; - if(unlikely(!d->st_bytes)) { - d->st_bytes = rrdset_find_bytype(RRD_TYPE_TC, d->id); - if(unlikely(!d->st_bytes)) { - debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id); - d->st_bytes = 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); - } - } - else { - debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_bytes); + if(unlikely(!d->st_bytes)) + d->st_bytes = 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, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); - if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) { - rrdset_set_name(d->st_bytes, d->name); - d->name_updated = 0; - } + else { + rrdset_next(d->st_bytes); + if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if((c->isleaf && c->hasparent && !c->isqdisc) || d->enabled_all_classes_qdiscs) { - if(unlikely(!c->rd_bytes)) { - c->rd_bytes = rrddim_find(d->st_bytes, c->id); - if(unlikely(!c->rd_bytes)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_bytes->id, c->id, c->name); + if(unlikely(!c->rd_bytes)) + c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); - // new class, we have to add it - c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_bytes->id, c->id); - } + rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); + } + rrdset_done(d->st_bytes); + } - rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); + // -------------------------------------------------------------------- + // packets - // 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_bytes->id, c->rd_bytes->id, c->name); - rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); - } - } - } - rrdset_done(d->st_bytes); - } + if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) { + d->enabled_packets = CONFIG_ONDEMAND_YES; - // -------------------------------------------------------------------- - // packets - - if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) { - d->enabled_packets = CONFIG_ONDEMAND_YES; + if(unlikely(!d->st_packets)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); - if(unlikely(!d->st_packets)) { - char id[RRD_ID_LENGTH_MAX + 1]; + d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); + } + else { + rrdset_next(d->st_packets); + + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); - - d->st_packets = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_packets)) { - debug(D_TC_LOOP, "TC: Creating new _packets chart for device '%s'", d->name?d->name:d->id); - d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, RRDSET_TYPE_STACKED); - } + rrdset_set_name(d->st_packets, name); } - else { - debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_packets); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if((c->isleaf && c->hasparent && !c->isqdisc) || d->enabled_all_classes_qdiscs) { - if(unlikely(!c->rd_packets)) { - c->rd_packets = rrddim_find(d->st_packets, c->id); - if(unlikely(!c->rd_packets)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_packets->id, c->id, c->name); + if(unlikely(!c->rd_packets)) + c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_packets, c->rd_packets, c->name); - // new class, we have to add it - c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_packets->id, c->id); - } + rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); + } + rrdset_done(d->st_packets); + } - rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); + // -------------------------------------------------------------------- + // dropped - // 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_packets->id, c->rd_packets->id, c->name); - rrddim_set_name(d->st_packets, c->rd_packets, c->name); - } - } - } - rrdset_done(d->st_packets); + if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) { + d->enabled_dropped = CONFIG_ONDEMAND_YES; + + if(unlikely(!d->st_dropped)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); + + d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); } + else { + rrdset_next(d->st_dropped); - // -------------------------------------------------------------------- - // dropped - - if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) { - d->enabled_dropped = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_dropped)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); - - d->st_dropped = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_dropped)) { - debug(D_TC_LOOP, "TC: Creating new _dropped chart for device '%s'", d->name?d->name:d->id); - d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, RRDSET_TYPE_STACKED); - } + rrdset_set_name(d->st_dropped, name); } - else { - debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_dropped); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if((c->isleaf && c->hasparent && !c->isqdisc) || d->enabled_all_classes_qdiscs) { - if(unlikely(!c->rd_dropped)) { - c->rd_dropped = rrddim_find(d->st_dropped, c->id); - if(unlikely(!c->rd_dropped)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_dropped->id, c->id, c->name); + if(unlikely(!c->rd_dropped)) + c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); - // new class, we have to add it - c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_dropped->id, c->id); - } + rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); + } + rrdset_done(d->st_dropped); + } - rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); + // -------------------------------------------------------------------- + // 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_dropped->id, c->rd_dropped->id, c->name); - rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); - } - } - } - rrdset_done(d->st_dropped); + 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_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 { + rrdset_next(d->st_tokens); - // -------------------------------------------------------------------- - // 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]; + if(unlikely(d->name_updated)) { 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); - } + rrdset_set_name(d->st_tokens, name); } - 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 + // FIXME + // update the family + } + + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; + + if(unlikely(!c->rd_tokens)) { + c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); } + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + } + rrdset_done(d->st_tokens); + } - if((c->isleaf && c->hasparent && !c->isqdisc) || d->enabled_all_classes_qdiscs) { - 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); + // -------------------------------------------------------------------- + // ctokens - // 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); - } + if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) { + d->enabled_ctokens = CONFIG_ONDEMAND_YES; - rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + 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); - // 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); + 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); - // -------------------------------------------------------------------- - // 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]; + if(unlikely(d->name_updated)) { 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 + rrdset_set_name(d->st_ctokens, name); } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; - - if((c->isleaf && c->hasparent && !c->isqdisc) || d->enabled_all_classes_qdiscs) { - 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); + // FIXME + // update the family + } - // 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); - } + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); + if(unlikely(!c->rd_ctokens)) + c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); - // 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); + rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); } + rrdset_done(d->st_ctokens); } tc_device_classes_cleanup(d); } -static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) -{ +static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) { + if(unlikely(!name || !*name)) return; + struct tc_class *c = tc_class_index_find(d, id, 0); if(likely(c)) { - freez(c->name); - c->name = NULL; + if(likely(c->name)) { + if(!strcmp(c->name, name)) return; + freez(c->name); + c->name = NULL; + } if(likely(name && *name && strcmp(c->id, name) != 0)) { debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name); @@ -597,10 +580,15 @@ static inline void tc_device_set_class_name(struct tc_device *d, char *id, char } static inline void tc_device_set_device_name(struct tc_device *d, char *name) { - freez(d->name); - d->name = NULL; + if(unlikely(!name || !*name)) return; + + if(d->name) { + if(!strcmp(d->name, name)) return; + freez(d->name); + d->name = NULL; + } - if(likely(name && *name && strcmp(d->id, name) != 0)) { + if(likely(name && *name && strcmp(d->id, name))) { debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name); d->name = strdupz(name); d->name_updated = 1; @@ -830,26 +818,48 @@ void *tc_main(void *ptr) { if(unlikely(device && ((first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) || (first_hash == QDISC_HASH && strcmp(words[0], "qdisc") == 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]); - // words[1] : class type - // words[2] : N:XX - // words[3] : parent or root - if(likely(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 + char *type = words[1]; // the class/qdisc type: htb, fq_codel, etc + char *id = words[2]; // the class/qdisc major:minor + char *parent = words[3]; // the word 'parent' or 'root' + char *parentid = words[4]; // parentid + char *leaf = words[5]; // the word 'leaf' + char *leafid = words[6]; // leafid + + int parent_is_root = 0; + int parent_is_parent = 0; + if(likely(parent)) { + parent_is_parent = !strcmp(parent, "parent"); - // we are only interested for HTB classes - //if(strcmp(type, "htb") != 0) continue; + if(!parent_is_parent) + parent_is_root = !strcmp(parent, "root"); + } - char *id = words[2]; // the class major:minor - char *parent = words[3]; // 'parent' or 'root' - char *parentid = words[4]; // the parent's id - char *leaf = words[5]; // 'leaf' - char *leafid = words[6]; // leafid + if(likely(type && id && (parent_is_root || parent_is_parent))) { char qdisc = 0; - if(strcmp(words[0], "qdisc") == 0) { + if(first_hash == QDISC_HASH) { qdisc = 1; + + if(!strcmp(type, "ingress")) { + // we don't want to get the ingress qdisc + // there should be an IFB interface for this + + class = NULL; + continue; + } + + if(parent_is_parent && parentid) { + // eliminate the minor number from parentid + // why: parentid is the id of the parent class + // but major: is also the id of the parent qdisc + + char *s = parentid; + while(*s && *s != ':') s++; + if(*s == ':') s[1] = '\0'; + } } - if(strcmp(parent, "root") == 0) { + + if(parent_is_root) { parentid = NULL; leafid = NULL; }