@cryptotaxi247 / netdata-1 / commits / b8d1ae8de

Updated tc.plugin (linux bandwidth QoS) (#13634)

* modernized tc plugins (replacing AVL with DICTIONARY, char * with STRING *, linked list with DICTIONARY) and added labels * replace custom metrics with worker metrics and add monitoring number of devices and classes * revert bool to char * Revert "revert bool to char" This reverts commit fae9b92dc0be4b00a5694f8b90972787879ae8a7. * fixed typo * removed bitfield from bool * commented unused function * commented unused function again

Costa Tsaousis committed Sep 7, 2022 at 16:48 UTC b8d1ae8de761c616f7e29e43677675c65c119996
2 files changed +389 -379
collectors/tc.plugin/plugin_tc.c
+388 -379
@@ -12,60 +12,46 @@
12 #define TC_LINE_MAX 1024
13
14 struct tc_class {
15 - avl_t avl;
15 + STRING *id;
16 + STRING *name;
17 + STRING *leafid;
18 + STRING *parentid;
19 +
20 + bool hasparent;
21 + bool isleaf;
22 + bool isqdisc;
23 + bool render;
24 + bool name_updated;
25 + bool updated;
26
17 - char *id;
18 - uint32_t hash;
19 -
20 - char *name;
21 -
22 - char *leafid;
23 - uint32_t leaf_hash;
24 -
25 - char *parentid;
26 - uint32_t parent_hash;
27 -
28 - char hasparent;
29 - char isleaf;
30 - char isqdisc;
31 - char render;
27 + int unupdated; // the number of times, this has been found un-updated
28
29 unsigned long long bytes;
30 unsigned long long packets;
31 unsigned long long dropped;
36 - unsigned long long overlimits;
37 - unsigned long long requeues;
38 - unsigned long long lended;
39 - unsigned long long borrowed;
40 - unsigned long long giants;
32 unsigned long long tokens;
33 unsigned long long ctokens;
34
35 + //unsigned long long overlimits;
36 + //unsigned long long requeues;
37 + //unsigned long long lended;
38 + //unsigned long long borrowed;
39 + //unsigned long long giants;
40 +
41 RRDDIM *rd_bytes;
42 RRDDIM *rd_packets;
43 RRDDIM *rd_dropped;
44 RRDDIM *rd_tokens;
45 RRDDIM *rd_ctokens;
49 -
50 - char name_updated;
51 - char updated; // updated bytes
52 - int unupdated; // the number of times, this has been found un-updated
53 -
54 - struct tc_class *next;
55 - struct tc_class *prev;
46 };
47
48 struct tc_device {
59 - avl_t avl;
60 -
61 - char *id;
62 - uint32_t hash;
49 + STRING *id;
50 + STRING *name;
51 + STRING *family;
52
64 - char *name;
65 - char *family;
66 -
67 - char name_updated;
68 - char family_updated;
53 + bool name_updated;
54 + bool family_updated;
55
56 char enabled;
57 char enabled_bytes;
@@ -81,94 +67,121 @@ struct tc_device {
67 RRDSET *st_tokens;
68 RRDSET *st_ctokens;
69
84 - avl_tree_type classes_index;
70 + DICTIONARY *classes;
71 +};
72
86 - struct tc_class *classes;
87 - struct tc_class *last_class;
73
89 - struct tc_device *next;
90 - struct tc_device *prev;
91 -};
74 +// ----------------------------------------------------------------------------
75 +// tc_class index
76
77 +static void tc_class_free_callback(const char *name __maybe_unused, void *value, void *data __maybe_unused) {
78 + // struct tc_device *d = data;
79 + struct tc_class *c = value;
80
94 -struct tc_device *tc_device_root = NULL;
81 + string_freez(c->id);
82 + string_freez(c->name);
83 + string_freez(c->leafid);
84 + string_freez(c->parentid);
85 +}
86
96 -// ----------------------------------------------------------------------------
97 -// tc_device index
87 +static void tc_class_conflict_callback(const char *name __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
88 + struct tc_device *d = data; (void)d;
89 + struct tc_class *c = old_value; (void)c;
90 + struct tc_class *new_c = new_value; (void)new_c;
91 +
92 + error("TC: class '%s' is already in device '%s'. Ignoring duplicate.", name, string2str(d->id));
93
99 -static int tc_device_compare(void* a, void* b) {
100 - if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1;
101 - else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1;
102 - else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id);
94 + tc_class_free_callback(name, new_value, data);
95 }
96
105 -avl_tree_type tc_device_root_index = {
106 - NULL,
107 - tc_device_compare
108 -};
97 +static void tc_class_index_init(struct tc_device *d) {
98 + if(!d->classes) {
99 + d->classes = dictionary_create(
100 + DICTIONARY_FLAG_DONT_OVERWRITE_VALUE
101 + |DICTIONARY_FLAG_SINGLE_THREADED
102 + );
103
110 -#define tc_device_index_add(st) (struct tc_device *)avl_insert(&tc_device_root_index, (avl_t *)(st))
111 -#define tc_device_index_del(st) (struct tc_device *)avl_remove(&tc_device_root_index, (avl_t *)(st))
104 + dictionary_register_delete_callback(d->classes, tc_class_free_callback, d);
105 + dictionary_register_conflict_callback(d->classes, tc_class_conflict_callback, d);
106 + }
107 +}
108
113 -static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) {
114 - struct tc_device tmp;
115 - tmp.id = (char *)id;
116 - tmp.hash = (hash)?hash:simple_hash(tmp.id);
109 +static void tc_class_index_destroy(struct tc_device *d) {
110 + dictionary_destroy(d->classes);
111 + d->classes = NULL;
112 +}
113
118 - return (struct tc_device *)avl_search(&(tc_device_root_index), (avl_t *)&tmp);
114 +static struct tc_class *tc_class_index_add(struct tc_device *d, struct tc_class *c) {
115 + return dictionary_set(d->classes, string2str(c->id), c, sizeof(*c));
116 }
117
118 +static void tc_class_index_del(struct tc_device *d, struct tc_class *c) {
119 + dictionary_del(d->classes, string2str(c->id));
120 +}
121 +
122 +static inline struct tc_class *tc_class_index_find(struct tc_device *d, const char *id) {
123 + return dictionary_get(d->classes, id);
124 +}
125
126 // ----------------------------------------------------------------------------
123 -// tc_class index
127 +// tc_device index
128 +
129 +static DICTIONARY *tc_device_root_index = NULL;
130
125 -static int tc_class_compare(void* a, void* b) {
126 - if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1;
127 - else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1;
128 - else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
131 +static void tc_device_add_callback(const char *name __maybe_unused, void *value, void *data __maybe_unused) {
132 + struct tc_device *d = value;
133 + tc_class_index_init(d);
134 }
135
131 -#define tc_class_index_add(st, rd) (struct tc_class *)avl_insert(&((st)->classes_index), (avl_t *)(rd))
132 -#define tc_class_index_del(st, rd) (struct tc_class *)avl_remove(&((st)->classes_index), (avl_t *)(rd))
136 +static void tc_device_free_callback(const char *name __maybe_unused, void *value, void *data __maybe_unused) {
137 + struct tc_device *d = value;
138
134 -static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) {
135 - struct tc_class tmp;
136 - tmp.id = (char *)id;
137 - tmp.hash = (hash)?hash:simple_hash(tmp.id);
139 + tc_class_index_destroy(d);
140
139 - return (struct tc_class *)avl_search(&(st->classes_index), (avl_t *) &tmp);
141 + string_freez(d->id);
142 + string_freez(d->name);
143 + string_freez(d->family);
144 }
145
142 -// ----------------------------------------------------------------------------
146 +static void tc_device_index_init() {
147 + if(!tc_device_root_index) {
148 + tc_device_root_index = dictionary_create(
149 + DICTIONARY_FLAG_DONT_OVERWRITE_VALUE
150 + |DICTIONARY_FLAG_SINGLE_THREADED
151 + |DICTIONARY_FLAG_ADD_IN_FRONT
152 + );
153
144 -static inline void tc_class_free(struct tc_device *n, struct tc_class *c) {
145 - if(c == n->classes) {
146 - if(likely(c->next))
147 - n->classes = c->next;
148 - else
149 - n->classes = c->prev;
154 + dictionary_register_insert_callback(tc_device_root_index, tc_device_add_callback, NULL);
155 + dictionary_register_delete_callback(tc_device_root_index, tc_device_free_callback, NULL);
156 }
157 +}
158
152 - if(c == n->last_class) {
153 - if(unlikely(c->next))
154 - n->last_class = c->next;
155 - else
156 - n->last_class = c->prev;
157 - }
159 +static void tc_device_index_destroy() {
160 + dictionary_destroy(tc_device_root_index);
161 + tc_device_root_index = NULL;
162 +}
163
159 - if(c->next) c->next->prev = c->prev;
160 - if(c->prev) c->prev->next = c->next;
164 +static struct tc_device *tc_device_index_add(struct tc_device *d) {
165 + return dictionary_set(tc_device_root_index, string2str(d->id), d, sizeof(*d));
166 +}
167
162 - debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', unused=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->unupdated);
168 +//static struct tc_device *tc_device_index_del(struct tc_device *d) {
169 +// dictionary_del(tc_device_root_index, string2str(d->id));
170 +// return d;
171 +//}
172
164 - if(unlikely(tc_class_index_del(n, c) != c))
165 - error("plugin_tc: INTERNAL ERROR: attempt remove class '%s' from device '%s': removed a different calls", c->id, n->id);
173 +static inline struct tc_device *tc_device_index_find(const char *id) {
174 + return dictionary_get(tc_device_root_index, id);
175 +}
176
167 - freez(c->id);
168 - freez(c->name);
169 - freez(c->leafid);
170 - freez(c->parentid);
171 - freez(c);
177 +// ----------------------------------------------------------------------------
178 +
179 +static inline void tc_class_free(struct tc_device *n, struct tc_class *c) {
180 + debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', unused=%d",
181 + string2str(n->id), string2str(c->id), string2str(c->parentid), string2str(c->leafid),
182 + c->unupdated);
183 +
184 + tc_class_index_del(n, c);
185 }
186
187 static inline void tc_device_classes_cleanup(struct tc_device *d) {
@@ -179,23 +192,20 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) {
192 if(cleanup_every < 0) cleanup_every = -cleanup_every;
193 }
194
182 - d->name_updated = 0;
183 - d->family_updated = 0;
195 + d->name_updated = false;
196 + d->family_updated = false;
197
185 - struct tc_class *c = d->classes;
186 - while(c) {
187 - if(unlikely(cleanup_every && c->unupdated >= cleanup_every)) {
188 - struct tc_class *nc = c->next;
198 + struct tc_class *c;
199 + dfe_start_unsafe(d->classes, c) {
200 + if(unlikely(cleanup_every && c->unupdated >= cleanup_every))
201 tc_class_free(d, c);
190 - c = nc;
191 - }
192 - else {
193 - c->updated = 0;
194 - c->name_updated = 0;
202
196 - c = c->next;
203 + else {
204 + c->updated = false;
205 + c->name_updated = false;
206 }
207 }
208 + dfe_done(c);
209 }
210
211 static inline void tc_device_commit(struct tc_device *d) {
@@ -213,26 +223,26 @@ static inline void tc_device_commit(struct tc_device *d) {
223
224 if(unlikely(d->enabled == (char)-1)) {
225 char var_name[CONFIG_MAX_NAME + 1];
216 - snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
226 + snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", string2str(d->id));
227
228 d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
229
220 - snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id);
230 + snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", string2str(d->id));
231 d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes);
232
223 - snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id);
233 + snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", string2str(d->id));
234 d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets);
235
226 - snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
236 + snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", string2str(d->id));
237 d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
238
229 - snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id);
239 + snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", string2str(d->id));
240 d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens);
241
232 - snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id);
242 + snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", string2str(d->id));
243 d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens);
244
235 - snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id);
245 + snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", string2str(d->id));
246 d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs);
247 }
248
@@ -244,11 +254,10 @@ static inline void tc_device_commit(struct tc_device *d) {
254 // prepare all classes
255 // we set reasonable defaults for the rest of the code below
256
247 - for(c = d->classes ; c ; c = c->next) {
248 - c->render = 0; // do not render this class
249 -
250 - c->isleaf = 1; // this is a leaf class
251 - c->hasparent = 0; // without a parent
257 + dfe_start_unsafe(d->classes, c) {
258 + c->render = false; // do not render this class
259 + c->isleaf = true; // this is a leaf class
260 + c->hasparent = false; // without a parent
261
262 if(unlikely(!c->updated))
263 c->unupdated++; // increase its unupdated counter
@@ -262,21 +271,23 @@ static inline void tc_device_commit(struct tc_device *d) {
271 updated_classes++;
272 }
273 }
274 + dfe_done(c);
275
276 if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) {
267 - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id);
277 + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", string2str(d->name?d->name:d->id));
278 tc_device_classes_cleanup(d);
279 return;
280 }
281
282 if(unlikely(updated_classes && updated_qdiscs)) {
273 - error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs);
283 + error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", string2str(d->id), updated_classes, updated_qdiscs);
284
285 // set all classes to !updated
276 - for(c = d->classes ; c ; c = c->next)
277 - if(unlikely(!c->isqdisc && c->updated))
278 - c->updated = 0;
279 -
286 + dfe_start_unsafe(d->classes, c) {
287 + if (unlikely(!c->isqdisc && c->updated))
288 + c->updated = false;
289 + }
290 + dfe_done(c);
291 updated_classes = 0;
292 }
293
@@ -296,8 +307,9 @@ static inline void tc_device_commit(struct tc_device *d) {
307 // so, here we remove the isleaf flag from nodes in the middle
308 // and we add the hasparent flag to leaf nodes we found their parent
309 if(likely(!d->enabled_all_classes_qdiscs)) {
299 - for(c = d->classes; c; c = c->next) {
300 - if(unlikely(!c->updated)) continue;
310 + dfe_start_unsafe(d->classes, c) {
311 + if(unlikely(!c->updated))
312 + continue;
313
314 //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.",
315 // d->id,
@@ -307,30 +319,34 @@ static inline void tc_device_commit(struct tc_device *d) {
319 // c->parentid?c->parentid:"NULL");
320
321 // find if c is leaf or not
310 - for(x = d->classes; x; x = x->next) {
311 - if(unlikely(!x->updated || c == x || !x->parentid)) continue;
322 + dfe_start_unsafe(d->classes, x) {
323 + if(unlikely(!x->updated || c == x || !x->parentid))
324 + continue;
325
326 // classes have both parentid and leafid
327 // qdiscs have only parentid
328 // the following works for both (it is an OR)
329
317 - if((c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) ||
318 - (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0)) {
330 + if((x->parentid && c->id == x->parentid) ||
331 + (c->leafid && x->parentid && c->leafid == x->parentid)) {
332 // 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);
320 - c->isleaf = 0;
321 - x->hasparent = 1;
333 + c->isleaf = false;
334 + x->hasparent = true;
335 }
336 }
337 + dfe_done(x);
338 }
339 + dfe_done(c);
340 }
341
327 - for(c = d->classes ; c ; c = c->next) {
328 - if(unlikely(!c->updated)) continue;
342 + dfe_start_unsafe(d->classes, c) {
343 + if(unlikely(!c->updated))
344 + continue;
345
346 // 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);
347
348 if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) {
333 - c->render = 1;
349 + c->render = true;
350 active_nodes++;
351 bytes_sum += c->bytes;
352 packets_sum += c->packets;
@@ -345,26 +361,29 @@ static inline void tc_device_commit(struct tc_device *d) {
361 // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id);
362 //}
363 }
364 + dfe_done(c);
365
366 #ifdef NETDATA_INTERNAL_CHECKS
367 // dump all the list to see what we know
368
369 if(unlikely(debug_flags & D_TC_LOOP)) {
353 - for(c = d->classes ; c ; c = c->next) {
354 - if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id);
355 - 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)");
370 + dfe_start_unsafe(d->classes, c) {
371 + if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", string2str(d->name), string2str(c->id));
372 + else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class '%s', IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: '%s')",
373 + string2str(d->name?d->name:d->id), string2str(c->id), c->updated, c->isleaf, c->hasparent, string2str(c->parentid));
374 }
375 + dfe_done(c);
376 }
377 #endif
378
379 if(unlikely(!active_nodes)) {
361 - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id);
380 + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", string2str(d->name?d->name:d->id));
381 tc_device_classes_cleanup(d);
382 return;
383 }
384
385 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).",
367 - d->name?d->name:d->id,
386 + string2str(d->name?d->name:d->id),
387 d->enabled, enable_new_interfaces,
388 d->enabled_bytes, enable_bytes,
389 d->enabled_packets, enable_packets,
@@ -383,44 +402,54 @@ static inline void tc_device_commit(struct tc_device *d) {
402 // bytes
403
404 if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO &&
386 - (bytes_sum ||
387 - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) {
405 + (bytes_sum || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) {
406 d->enabled_bytes = CONFIG_BOOLEAN_YES;
407
390 - if(unlikely(!d->st_bytes))
408 + if(unlikely(!d->st_bytes)) {
409 d->st_bytes = rrdset_create_localhost(
392 - RRD_TYPE_TC
393 - , d->id
394 - , d->name ? d->name : d->id
395 - , d->family ? d->family : d->id
396 - , RRD_TYPE_TC ".qos"
397 - , "Class Usage"
398 - , "kilobits/s"
399 - , PLUGIN_TC_NAME
400 - , NULL
401 - , NETDATA_CHART_PRIO_TC_QOS
402 - , localhost->rrd_update_every
403 - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED
404 - );
405 -
410 + RRD_TYPE_TC,
411 + string2str(d->id),
412 + string2str(d->name ? d->name : d->id),
413 + string2str(d->family ? d->family : d->id),
414 + RRD_TYPE_TC ".qos",
415 + "Class Usage",
416 + "kilobits/s",
417 + PLUGIN_TC_NAME,
418 + NULL,
419 + NETDATA_CHART_PRIO_TC_QOS,
420 + localhost->rrd_update_every,
421 + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
422 +
423 + rrdlabels_add(d->st_bytes->state->chart_labels, "device", string2str(d->id), RRDLABEL_SRC_AUTO);
424 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO);
425 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO);
426 + }
427 else {
428 rrdset_next(d->st_bytes);
408 - if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name);
429 + if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, string2str(d->name));
430 +
431 + if(d->name && d->name_updated)
432 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
433 +
434 + if(d->family && d->family_updated)
435 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family), RRDLABEL_SRC_AUTO);
436
437 // TODO
438 // update the family
439 }
440
414 - for(c = d->classes ; c ; c = c->next) {
441 + dfe_start_unsafe(d->classes, c) {
442 if(unlikely(!c->render)) continue;
443
444 if(unlikely(!c->rd_bytes))
418 - c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
445 + c->rd_bytes = rrddim_add(d->st_bytes, string2str(c->id), string2str(c->name?c->name:c->id), 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
446 else if(unlikely(c->name_updated))
420 - rrddim_set_name(d->st_bytes, c->rd_bytes, c->name);
447 + rrddim_set_name(d->st_bytes, c->rd_bytes, string2str(c->name));
448
449 rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes);
450 }
451 + dfe_done(c);
452 +
453 rrdset_done(d->st_bytes);
454 }
455
@@ -435,47 +464,58 @@ static inline void tc_device_commit(struct tc_device *d) {
464 if(unlikely(!d->st_packets)) {
465 char id[RRD_ID_LENGTH_MAX + 1];
466 char name[RRD_ID_LENGTH_MAX + 1];
438 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id);
439 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
467 + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->id));
468 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->name ? d->name : d->id));
469
470 d->st_packets = rrdset_create_localhost(
442 - RRD_TYPE_TC
443 - , id
444 - , name
445 - , d->family ? d->family : d->id
446 - , RRD_TYPE_TC ".qos_packets"
447 - , "Class Packets"
448 - , "packets/s"
449 - , PLUGIN_TC_NAME
450 - , NULL
451 - , NETDATA_CHART_PRIO_TC_QOS_PACKETS
452 - , localhost->rrd_update_every
453 - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED
454 - );
471 + RRD_TYPE_TC,
472 + id,
473 + name,
474 + string2str(d->family ? d->family : d->id),
475 + RRD_TYPE_TC ".qos_packets",
476 + "Class Packets",
477 + "packets/s",
478 + PLUGIN_TC_NAME,
479 + NULL,
480 + NETDATA_CHART_PRIO_TC_QOS_PACKETS,
481 + localhost->rrd_update_every,
482 + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
483 +
484 + rrdlabels_add(d->st_bytes->state->chart_labels, "device", string2str(d->id), RRDLABEL_SRC_AUTO);
485 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO);
486 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO);
487 }
488 else {
489 rrdset_next(d->st_packets);
490
491 if(unlikely(d->name_updated)) {
492 char name[RRD_ID_LENGTH_MAX + 1];
461 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
493 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->name?d->name:d->id));
494 rrdset_set_name(d->st_packets, name);
495 }
496
497 + if(d->name && d->name_updated)
498 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
499 +
500 + if(d->family && d->family_updated)
501 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family), RRDLABEL_SRC_AUTO);
502 +
503 // TODO
504 // update the family
505 }
506
469 - for(c = d->classes ; c ; c = c->next) {
507 + dfe_start_unsafe(d->classes, c) {
508 if(unlikely(!c->render)) continue;
509
510 if(unlikely(!c->rd_packets))
473 - c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL);
511 + c->rd_packets = rrddim_add(d->st_packets, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_INCREMENTAL);
512 else if(unlikely(c->name_updated))
475 - rrddim_set_name(d->st_packets, c->rd_packets, c->name);
513 + rrddim_set_name(d->st_packets, c->rd_packets, string2str(c->name));
514
515 rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets);
516 }
517 + dfe_done(c);
518 +
519 rrdset_done(d->st_packets);
520 }
521
@@ -490,47 +530,58 @@ static inline void tc_device_commit(struct tc_device *d) {
530 if(unlikely(!d->st_dropped)) {
531 char id[RRD_ID_LENGTH_MAX + 1];
532 char name[RRD_ID_LENGTH_MAX + 1];
493 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id);
494 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
533 + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->id));
534 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->name ? d->name : d->id));
535
536 d->st_dropped = rrdset_create_localhost(
497 - RRD_TYPE_TC
498 - , id
499 - , name
500 - , d->family ? d->family : d->id
501 - , RRD_TYPE_TC ".qos_dropped"
502 - , "Class Dropped Packets"
503 - , "packets/s"
504 - , PLUGIN_TC_NAME
505 - , NULL
506 - , NETDATA_CHART_PRIO_TC_QOS_DROPPED
507 - , localhost->rrd_update_every
508 - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED
509 - );
537 + RRD_TYPE_TC,
538 + id,
539 + name,
540 + string2str(d->family ? d->family : d->id),
541 + RRD_TYPE_TC ".qos_dropped",
542 + "Class Dropped Packets",
543 + "packets/s",
544 + PLUGIN_TC_NAME,
545 + NULL,
546 + NETDATA_CHART_PRIO_TC_QOS_DROPPED,
547 + localhost->rrd_update_every,
548 + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
549 +
550 + rrdlabels_add(d->st_bytes->state->chart_labels, "device", string2str(d->id), RRDLABEL_SRC_AUTO);
551 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO);
552 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO);
553 }
554 else {
555 rrdset_next(d->st_dropped);
556
557 if(unlikely(d->name_updated)) {
558 char name[RRD_ID_LENGTH_MAX + 1];
516 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
559 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->name?d->name:d->id));
560 rrdset_set_name(d->st_dropped, name);
561 }
562
563 + if(d->name && d->name_updated)
564 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
565 +
566 + if(d->family && d->family_updated)
567 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family), RRDLABEL_SRC_AUTO);
568 +
569 // TODO
570 // update the family
571 }
572
524 - for(c = d->classes ; c ; c = c->next) {
573 + dfe_start_unsafe(d->classes, c) {
574 if(unlikely(!c->render)) continue;
575
576 if(unlikely(!c->rd_dropped))
528 - c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL);
577 + c->rd_dropped = rrddim_add(d->st_dropped, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_INCREMENTAL);
578 else if(unlikely(c->name_updated))
530 - rrddim_set_name(d->st_dropped, c->rd_dropped, c->name);
579 + rrddim_set_name(d->st_dropped, c->rd_dropped, string2str(c->name));
580
581 rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped);
582 }
583 + dfe_done(c);
584 +
585 rrdset_done(d->st_dropped);
586 }
587
@@ -545,48 +596,59 @@ static inline void tc_device_commit(struct tc_device *d) {
596 if(unlikely(!d->st_tokens)) {
597 char id[RRD_ID_LENGTH_MAX + 1];
598 char name[RRD_ID_LENGTH_MAX + 1];
548 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id);
549 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id);
599 + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->id));
600 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->name ? d->name : d->id));
601
602 d->st_tokens = rrdset_create_localhost(
552 - RRD_TYPE_TC
553 - , id
554 - , name
555 - , d->family ? d->family : d->id
556 - , RRD_TYPE_TC ".qos_tokens"
557 - , "Class Tokens"
558 - , "tokens"
559 - , PLUGIN_TC_NAME
560 - , NULL
561 - , NETDATA_CHART_PRIO_TC_QOS_TOKENS
562 - , localhost->rrd_update_every
563 - , RRDSET_TYPE_LINE
564 - );
603 + RRD_TYPE_TC,
604 + id,
605 + name,
606 + string2str(d->family ? d->family : d->id),
607 + RRD_TYPE_TC ".qos_tokens",
608 + "Class Tokens",
609 + "tokens",
610 + PLUGIN_TC_NAME,
611 + NULL,
612 + NETDATA_CHART_PRIO_TC_QOS_TOKENS,
613 + localhost->rrd_update_every,
614 + RRDSET_TYPE_LINE);
615 +
616 + rrdlabels_add(d->st_bytes->state->chart_labels, "device", string2str(d->id), RRDLABEL_SRC_AUTO);
617 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO);
618 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO);
619 }
620 else {
621 rrdset_next(d->st_tokens);
622
623 if(unlikely(d->name_updated)) {
624 char name[RRD_ID_LENGTH_MAX + 1];
571 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id);
625 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->name?d->name:d->id));
626 rrdset_set_name(d->st_tokens, name);
627 }
628
629 + if(d->name && d->name_updated)
630 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
631 +
632 + if(d->family && d->family_updated)
633 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family), RRDLABEL_SRC_AUTO);
634 +
635 // TODO
636 // update the family
637 }
638
579 - for(c = d->classes ; c ; c = c->next) {
639 + dfe_start_unsafe(d->classes, c) {
640 if(unlikely(!c->render)) continue;
641
642 if(unlikely(!c->rd_tokens)) {
583 - c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE);
643 + c->rd_tokens = rrddim_add(d->st_tokens, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_ABSOLUTE);
644 }
645 else if(unlikely(c->name_updated))
586 - rrddim_set_name(d->st_tokens, c->rd_tokens, c->name);
646 + rrddim_set_name(d->st_tokens, c->rd_tokens, string2str(c->name));
647
648 rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens);
649 }
650 + dfe_done(c);
651 +
652 rrdset_done(d->st_tokens);
653 }
654
@@ -601,48 +663,59 @@ static inline void tc_device_commit(struct tc_device *d) {
663 if(unlikely(!d->st_ctokens)) {
664 char id[RRD_ID_LENGTH_MAX + 1];
665 char name[RRD_ID_LENGTH_MAX + 1];
604 - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id);
605 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id);
666 + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->id));
667 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->name ? d->name : d->id));
668
669 d->st_ctokens = rrdset_create_localhost(
608 - RRD_TYPE_TC
609 - , id
610 - , name
611 - , d->family ? d->family : d->id
612 - , RRD_TYPE_TC ".qos_ctokens"
613 - , "Class cTokens"
614 - , "ctokens"
615 - , PLUGIN_TC_NAME
616 - , NULL
617 - , NETDATA_CHART_PRIO_TC_QOS_CTOKENS
618 - , localhost->rrd_update_every
619 - , RRDSET_TYPE_LINE
620 - );
670 + RRD_TYPE_TC,
671 + id,
672 + name,
673 + string2str(d->family ? d->family : d->id),
674 + RRD_TYPE_TC ".qos_ctokens",
675 + "Class cTokens",
676 + "ctokens",
677 + PLUGIN_TC_NAME,
678 + NULL,
679 + NETDATA_CHART_PRIO_TC_QOS_CTOKENS,
680 + localhost->rrd_update_every,
681 + RRDSET_TYPE_LINE);
682 +
683 + rrdlabels_add(d->st_bytes->state->chart_labels, "device", string2str(d->id), RRDLABEL_SRC_AUTO);
684 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO);
685 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO);
686 }
687 else {
623 - debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id);
688 + debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", string2str(d->name?d->name:d->id));
689 rrdset_next(d->st_ctokens);
690
691 if(unlikely(d->name_updated)) {
692 char name[RRD_ID_LENGTH_MAX + 1];
628 - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id);
693 + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->name?d->name:d->id));
694 rrdset_set_name(d->st_ctokens, name);
695 }
696
697 + if(d->name && d->name_updated)
698 + rrdlabels_add(d->st_bytes->state->chart_labels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
699 +
700 + if(d->family && d->family_updated)
701 + rrdlabels_add(d->st_bytes->state->chart_labels, "family", string2str(d->family), RRDLABEL_SRC_AUTO);
702 +
703 // TODO
704 // update the family
705 }
706
636 - for(c = d->classes ; c ; c = c->next) {
707 + dfe_start_unsafe(d->classes, c) {
708 if(unlikely(!c->render)) continue;
709
710 if(unlikely(!c->rd_ctokens))
640 - c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE);
711 + c->rd_ctokens = rrddim_add(d->st_ctokens, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_ABSOLUTE);
712 else if(unlikely(c->name_updated))
642 - rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name);
713 + rrddim_set_name(d->st_ctokens, c->rd_ctokens, string2str(c->name));
714
715 rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens);
716 }
717 + dfe_done(c);
718 +
719 rrdset_done(d->st_ctokens);
720 }
721
@@ -652,18 +725,18 @@ static inline void tc_device_commit(struct tc_device *d) {
725 static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) {
726 if(unlikely(!name || !*name)) return;
727
655 - struct tc_class *c = tc_class_index_find(d, id, 0);
728 + struct tc_class *c = tc_class_index_find(d, id);
729 if(likely(c)) {
730 if(likely(c->name)) {
658 - if(!strcmp(c->name, name)) return;
659 - freez(c->name);
731 + if(!strcmp(string2str(c->name), name)) return;
732 + string_freez(c->name);
733 c->name = NULL;
734 }
735
663 - if(likely(name && *name && strcmp(c->id, name) != 0)) {
664 - debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
665 - c->name = strdupz(name);
666 - c->name_updated = 1;
736 + if(likely(name && *name && strcmp(string2str(c->id), name) != 0)) {
737 + debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", string2str(d->id), id, name);
738 + c->name = string_strdupz(name);
739 + c->name_updated = true;
740 }
741 }
742 }
@@ -672,124 +745,68 @@ static inline void tc_device_set_device_name(struct tc_device *d, char *name) {
745 if(unlikely(!name || !*name)) return;
746
747 if(d->name) {
675 - if(!strcmp(d->name, name)) return;
676 - freez(d->name);
748 + if(!strcmp(string2str(d->name), name)) return;
749 + string_freez(d->name);
750 d->name = NULL;
751 }
752
680 - if(likely(name && *name && strcmp(d->id, name) != 0)) {
681 - debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
682 - d->name = strdupz(name);
683 - d->name_updated = 1;
753 + if(likely(name && *name && strcmp(string2str(d->id), name) != 0)) {
754 + debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", string2str(d->id), name);
755 + d->name = string_strdupz(name);
756 + d->name_updated = true;
757 }
758 }
759
760 static inline void tc_device_set_device_family(struct tc_device *d, char *family) {
688 - freez(d->family);
761 + string_freez(d->family);
762 d->family = NULL;
763
691 - if(likely(family && *family && strcmp(d->id, family) != 0)) {
692 - debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
693 - d->family = strdupz(family);
694 - d->family_updated = 1;
764 + if(likely(family && *family && strcmp(string2str(d->id), family) != 0)) {
765 + debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", string2str(d->id), family);
766 + d->family = string_strdupz(family);
767 + d->family_updated = true;
768 }
769 // no need for null termination - it is already null
770 }
771
699 -static inline struct tc_device *tc_device_create(char *id)
700 -{
701 - struct tc_device *d = tc_device_index_find(id, 0);
772 +static inline struct tc_device *tc_device_create(char *id) {
773 + struct tc_device *d = tc_device_index_find(id);
774
775 if(!d) {
776 debug(D_TC_LOOP, "TC: Creating device '%s'", id);
777
706 - d = callocz(1, sizeof(struct tc_device));
707 -
708 - d->id = strdupz(id);
709 - d->hash = simple_hash(d->id);
710 - d->enabled = (char)-1;
711 -
712 - avl_init(&d->classes_index, tc_class_compare);
713 - if(unlikely(tc_device_index_add(d) != d))
714 - error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", d->id);
715 -
716 - if(!tc_device_root) {
717 - tc_device_root = d;
718 - }
719 - else {
720 - d->next = tc_device_root;
721 - tc_device_root->prev = d;
722 - tc_device_root = d;
723 - }
778 + struct tc_device tmp = {
779 + .id = string_strdupz(id),
780 + .enabled = (char)-1,
781 + };
782 + d = tc_device_index_add(&tmp);
783 }
784
785 return(d);
786 }
787
729 -static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char qdisc, char *parentid, char *leafid)
730 -{
731 - struct tc_class *c = tc_class_index_find(n, id, 0);
788 +static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, bool qdisc, char *parentid, char *leafid) {
789 + struct tc_class *c = tc_class_index_find(n, id);
790
791 if(!c) {
734 - debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
735 -
736 - c = callocz(1, sizeof(struct tc_class));
792 + debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'",
793 + string2str(n->id), id, parentid?parentid:"", leafid?leafid:"");
794
738 - if(unlikely(!n->classes))
739 - n->classes = c;
740 -
741 - else if(likely(n->last_class)) {
742 - n->last_class->next = c;
743 - c->prev = n->last_class;
744 - }
795 + struct tc_class tmp = {
796 + .id = string_strdupz(id),
797 + .isqdisc = qdisc,
798 + .parentid = string_strdupz(parentid),
799 + .leafid = string_strdupz(leafid),
800 + };
801
746 - n->last_class = c;
747 -
748 - c->id = strdupz(id);
749 - c->hash = simple_hash(c->id);
750 -
751 - c->isqdisc = qdisc;
752 - if(parentid && *parentid) {
753 - c->parentid = strdupz(parentid);
754 - c->parent_hash = simple_hash(c->parentid);
755 - }
756 -
757 - if(leafid && *leafid) {
758 - c->leafid = strdupz(leafid);
759 - c->leaf_hash = simple_hash(c->leafid);
760 - }
761 -
762 - if(unlikely(tc_class_index_add(n, c) != c))
763 - error("plugin_tc: INTERNAL ERROR: attempt index class '%s' on device '%s': already exists", c->id, n->id);
802 + tc_class_index_add(n, &tmp);
803 }
804 return(c);
805 }
806
768 -static inline void tc_device_free(struct tc_device *n)
769 -{
770 - if(n->next) n->next->prev = n->prev;
771 - if(n->prev) n->prev->next = n->next;
772 - if(tc_device_root == n) {
773 - if(n->next) tc_device_root = n->next;
774 - else tc_device_root = n->prev;
775 - }
776 -
777 - if(unlikely(tc_device_index_del(n) != n))
778 - error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", n->id);
779 -
780 - while(n->classes) tc_class_free(n, n->classes);
781 -
782 - freez(n->id);
783 - freez(n->name);
784 - freez(n->family);
785 - freez(n);
786 -}
787 -
788 -static inline void tc_device_free_all()
789 -{
790 - while(tc_device_root)
791 - tc_device_free(tc_device_root);
792 -}
807 +//static inline void tc_device_free(struct tc_device *d) {
808 +// tc_device_index_del(d);
809 +//}
810
811 #define PLUGINSD_MAX_WORDS 20
812
@@ -846,6 +863,8 @@ static pid_t tc_child_pid = 0;
863 static void tc_main_cleanup(void *ptr) {
864 worker_unregister();
865
866 + tc_device_index_destroy();
867 +
868 struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
869 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
870
@@ -876,8 +895,11 @@ static void tc_main_cleanup(void *ptr) {
895 #define WORKER_TC_SETDEVICEGROUP 7
896 #define WORKER_TC_SETCLASSNAME 8
897 #define WORKER_TC_WORKTIME 9
898 +#define WORKER_TC_PLUGIN_TIME 10
899 +#define WORKER_TC_DEVICES 11
900 +#define WORKER_TC_CLASSES 12
901
880 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 10
902 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 13
903 #error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10
904 #endif
905
@@ -894,6 +916,11 @@ void *tc_main(void *ptr) {
916 worker_register_job_name(WORKER_TC_SETCLASSNAME, "classname");
917 worker_register_job_name(WORKER_TC_WORKTIME, "worktime");
918
919 + worker_register_job_custom_metric(WORKER_TC_PLUGIN_TIME, "tc script execution time", "milliseconds/run", WORKER_METRIC_ABSOLUTE);
920 + worker_register_job_custom_metric(WORKER_TC_DEVICES, "number of devices", "devices", WORKER_METRIC_ABSOLUTE);
921 + worker_register_job_custom_metric(WORKER_TC_CLASSES, "number of classes", "classes", WORKER_METRIC_ABSOLUTE);
922 +
923 + tc_device_index_init();
924 netdata_thread_cleanup_push(tc_main_cleanup, ptr);
925
926 char command[FILENAME_MAX + 1];
@@ -969,10 +996,10 @@ void *tc_main(void *ptr) {
996 }
997
998 if(likely(type && id && (parent_is_root || parent_is_parent))) {
972 - char qdisc = 0;
999 + bool qdisc = false;
1000
1001 if(first_hash == QDISC_HASH) {
975 - qdisc = 1;
1002 + qdisc = true;
1003
1004 if(!strcmp(type, "ingress")) {
1005 // we don't want to get the ingress qdisc
@@ -1051,10 +1078,10 @@ void *tc_main(void *ptr) {
1078 // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
1079 if(likely(words[1] && *words[1])) {
1080 class->bytes = str2ull(words[1]);
1054 - class->updated = 1;
1081 + class->updated = true;
1082 }
1083 else {
1057 - class->updated = 0;
1084 + class->updated = false;
1085 }
1086
1087 if(likely(words[3] && *words[3]))
@@ -1063,24 +1090,24 @@ void *tc_main(void *ptr) {
1090 if(likely(words[6] && *words[6]))
1091 class->dropped = str2ull(words[6]);
1092
1066 - if(likely(words[8] && *words[8]))
1067 - class->overlimits = str2ull(words[8]);
1093 + //if(likely(words[8] && *words[8]))
1094 + // class->overlimits = str2ull(words[8]);
1095
1069 - if(likely(words[10] && *words[10]))
1070 - class->requeues = str2ull(words[8]);
1096 + //if(likely(words[10] && *words[10]))
1097 + // class->requeues = str2ull(words[8]);
1098 }
1099 else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) {
1100 worker_is_busy(WORKER_TC_LENDED);
1101
1102 // debug(D_TC_LOOP, "LENDED line '%s'", words[1]);
1076 - if(likely(words[1] && *words[1]))
1077 - class->lended = str2ull(words[1]);
1103 + //if(likely(words[1] && *words[1]))
1104 + // class->lended = str2ull(words[1]);
1105
1079 - if(likely(words[3] && *words[3]))
1080 - class->borrowed = str2ull(words[3]);
1106 + //if(likely(words[3] && *words[3]))
1107 + // class->borrowed = str2ull(words[3]);
1108
1082 - if(likely(words[5] && *words[5]))
1083 - class->giants = str2ull(words[5]);
1109 + //if(likely(words[5] && *words[5]))
1110 + // class->giants = str2ull(words[5]);
1111 }
1112 else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) {
1113 worker_is_busy(WORKER_TC_TOKENS);
@@ -1117,33 +1144,19 @@ void *tc_main(void *ptr) {
1144 }
1145 else if(unlikely(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0)) {
1146 worker_is_busy(WORKER_TC_WORKTIME);
1147 + worker_set_metric(WORKER_TC_PLUGIN_TIME, str2ll(words[1], NULL));
1148
1121 - // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
1122 - static RRDSET *sttime = NULL;
1123 - static RRDDIM *rd_run_time = NULL;
1124 -
1125 - if(unlikely(!sttime)) {
1126 - sttime = rrdset_create_localhost(
1127 - "netdata"
1128 - , "plugin_tc_time"
1129 - , NULL
1130 - , "workers plugin tc"
1131 - , "netdata.workers.tc.script_time"
1132 - , "Netdata TC script execution"
1133 - , "milliseconds/run"
1134 - , PLUGIN_TC_NAME
1135 - , NULL
1136 - , NETDATA_CHART_PRIO_NETDATA_TC_TIME
1137 - , localhost->rrd_update_every
1138 - , RRDSET_TYPE_AREA
1139 - );
1140 - rd_run_time = rrddim_add(sttime, "run_time", "run time", 1, 1, RRD_ALGORITHM_ABSOLUTE);
1141 - }
1142 - else rrdset_next(sttime);
1149 + size_t number_of_devices = dictionary_stats_entries(tc_device_root_index);
1150 + size_t number_of_classes = 0;
1151
1144 - rrddim_set_by_pointer(sttime, rd_run_time, str2ll(words[1], NULL));
1145 - rrdset_done(sttime);
1152 + struct tc_device *d;
1153 + dfe_start_unsafe(tc_device_root_index, d) {
1154 + number_of_classes += dictionary_stats_entries(d->classes);
1155 + }
1156 + dfe_done(d);
1157
1158 + worker_set_metric(WORKER_TC_DEVICES, number_of_devices);
1159 + worker_set_metric(WORKER_TC_CLASSES, number_of_classes);
1160 }
1161 //else {
1162 // debug(D_TC_LOOP, "IGNORED line");
@@ -1162,17 +1175,13 @@ void *tc_main(void *ptr) {
1175 class = NULL;
1176 }
1177
1165 - if(unlikely(netdata_exit)) {
1166 - tc_device_free_all();
1178 + if(unlikely(netdata_exit))
1179 goto cleanup;
1168 - }
1180
1181 if(code == 1 || code == 127) {
1182 // 1 = DISABLE
1183 // 127 = cannot even run it
1184 error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
1174 -
1175 - tc_device_free_all();
1185 goto cleanup;
1186 }
1187
libnetdata/dictionary/dictionary.h
+1
@@ -199,6 +199,7 @@ typedef DICTFE_CONST struct dictionary_foreach {
199 #define dfe_start_read(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_READ)
200 #define dfe_start_write(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_WRITE)
201 #define dfe_start_reentrant(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_REENTRANT)
202 +#define dfe_start_unsafe(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_NONE)
203 #define dfe_start_rw(dict, value, mode) \
204 do { \
205 DICTFE value ## _dfe = {}; \