blob: 75c3986f3b915b960365fba8fcc88d7490449c9e [file] [log] [blame]
Damjan Marion8973b072022-03-01 15:51:18 +01001/* SPDX-License-Identifier: Apache-2.0
2 * Copyright(c) 2022 Cisco Systems, Inc.
3 */
4
5#include <vlib/vlib.h>
6#include <vlib/stats/stats.h>
7
8vlib_stats_main_t vlib_stats_main;
9
Damjan Marion8973b072022-03-01 15:51:18 +010010void
11vlib_stats_segment_lock (void)
12{
Damjan Mariond1bd9af2022-03-14 13:04:38 +010013 vlib_main_t *vm = vlib_get_main ();
Damjan Marion8973b072022-03-01 15:51:18 +010014 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Mariond1bd9af2022-03-14 13:04:38 +010015
16 /* already locked by us */
17 if (sm->shared_header->in_progress &&
18 vm->thread_index == sm->locking_thread_index)
19 goto done;
20
21 ASSERT (sm->locking_thread_index == ~0);
22 ASSERT (sm->shared_header->in_progress == 0);
23 ASSERT (sm->n_locks == 0);
24
Damjan Marion8973b072022-03-01 15:51:18 +010025 clib_spinlock_lock (sm->stat_segment_lockp);
Damjan Mariond1bd9af2022-03-14 13:04:38 +010026
Damjan Marion8973b072022-03-01 15:51:18 +010027 sm->shared_header->in_progress = 1;
Damjan Mariond1bd9af2022-03-14 13:04:38 +010028 sm->locking_thread_index = vm->thread_index;
29done:
30 sm->n_locks++;
Damjan Marion8973b072022-03-01 15:51:18 +010031}
32
33void
34vlib_stats_segment_unlock (void)
35{
Damjan Mariond1bd9af2022-03-14 13:04:38 +010036 vlib_main_t *vm = vlib_get_main ();
Damjan Marion8973b072022-03-01 15:51:18 +010037 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Mariond1bd9af2022-03-14 13:04:38 +010038
39 ASSERT (sm->shared_header->in_progress == 1);
40 ASSERT (sm->locking_thread_index == vm->thread_index);
41 ASSERT (sm->n_locks > 0);
42
43 sm->n_locks--;
44
45 if (sm->n_locks > 0)
46 return;
47
Damjan Marion8973b072022-03-01 15:51:18 +010048 sm->shared_header->epoch++;
Damjan Mariond1bd9af2022-03-14 13:04:38 +010049 __atomic_store_n (&sm->shared_header->in_progress, 0, __ATOMIC_RELEASE);
50 sm->locking_thread_index = ~0;
Damjan Marion8973b072022-03-01 15:51:18 +010051 clib_spinlock_unlock (sm->stat_segment_lockp);
52}
53
54/*
55 * Change heap to the stats shared memory segment
56 */
57void *
58vlib_stats_set_heap ()
59{
60 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
61
62 ASSERT (sm && sm->shared_header);
63 return clib_mem_set_heap (sm->heap);
64}
65
66u32
67vlib_stats_find_entry_index (char *fmt, ...)
68{
69 u8 *name;
70 va_list va;
71
72 va_start (va, fmt);
73 name = va_format (0, fmt, &va);
74 va_end (va);
75 vec_add1 (name, 0);
76
77 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
78 hash_pair_t *hp = hash_get_pair (sm->directory_vector_by_name, name);
79 vec_free (name);
80 return hp ? hp->value[0] : STAT_SEGMENT_INDEX_INVALID;
81}
82
83static void
84hash_set_str_key_alloc (uword **h, const char *key, uword v)
85{
86 int size = strlen (key) + 1;
87 void *copy = clib_mem_alloc (size);
88 clib_memcpy_fast (copy, key, size);
89 hash_set_mem (*h, copy, v);
90}
91
92static void
93hash_unset_str_key_free (uword **h, const char *key)
94{
95 hash_pair_t *hp = hash_get_pair_mem (*h, key);
96 if (hp)
97 {
98 void *_k = uword_to_pointer (hp->key, void *);
99 hash_unset_mem (*h, _k);
100 clib_mem_free (_k);
101 }
102}
103
104u32
105vlib_stats_create_counter (vlib_stats_entry_t *e)
106{
107 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
108 void *oldheap;
109 u32 index = ~0;
110 int i;
111
112 oldheap = clib_mem_set_heap (sm->heap);
113 vec_foreach_index_backwards (i, sm->directory_vector)
114 if (sm->directory_vector[i].type == STAT_DIR_TYPE_EMPTY)
115 {
116 index = i;
117 break;
118 }
119
120 index = index == ~0 ? vec_len (sm->directory_vector) : index;
121
122 vec_validate (sm->directory_vector, index);
123 sm->directory_vector[index] = *e;
124
125 clib_mem_set_heap (oldheap);
126 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
127
128 return index;
129}
130
131void
132vlib_stats_remove_entry (u32 entry_index)
133{
134 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
135 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
136 void *oldheap;
137 counter_t **c;
Damjan Marion58fd4812022-03-14 13:04:38 +0100138 vlib_counter_t **vc;
Damjan Marion8973b072022-03-01 15:51:18 +0100139 u32 i;
140
141 if (entry_index >= vec_len (sm->directory_vector))
142 return;
143
144 oldheap = clib_mem_set_heap (sm->heap);
145
Damjan Marion58fd4812022-03-14 13:04:38 +0100146 vlib_stats_segment_lock ();
147
Damjan Marion8973b072022-03-01 15:51:18 +0100148 switch (e->type)
149 {
150 case STAT_DIR_TYPE_NAME_VECTOR:
151 for (i = 0; i < vec_len (e->string_vector); i++)
152 vec_free (e->string_vector[i]);
153 vec_free (e->string_vector);
154 break;
155
156 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
157 c = e->data;
158 e->data = 0;
159 for (i = 0; i < vec_len (c); i++)
160 vec_free (c[i]);
161 vec_free (c);
162 break;
163
Damjan Marion58fd4812022-03-14 13:04:38 +0100164 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
165 vc = e->data;
166 e->data = 0;
167 for (i = 0; i < vec_len (vc); i++)
168 vec_free (vc[i]);
169 vec_free (vc);
170 break;
171
Damjan Marion8973b072022-03-01 15:51:18 +0100172 case STAT_DIR_TYPE_SCALAR_INDEX:
173 case STAT_DIR_TYPE_SYMLINK:
174 break;
175 default:
176 ASSERT (0);
177 }
178
Damjan Marion58fd4812022-03-14 13:04:38 +0100179 vlib_stats_segment_unlock ();
180
Damjan Marion8973b072022-03-01 15:51:18 +0100181 clib_mem_set_heap (oldheap);
182 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
183
184 memset (e, 0, sizeof (*e));
185 e->type = STAT_DIR_TYPE_EMPTY;
186}
187
188static void
189vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
190{
191 u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
192
193 for (i = 0; i < len; i++)
194 {
195 e->name[i] = s[i];
196 if (s[i] == 0)
197 return;
198 }
199 ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
200 s[i] = 0;
201}
202
Damjan Marion58fd4812022-03-14 13:04:38 +0100203static u32
204vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
Damjan Marion8973b072022-03-01 15:51:18 +0100205{
Damjan Marion8973b072022-03-01 15:51:18 +0100206 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
207 vlib_stats_shared_header_t *shared_header = sm->shared_header;
Damjan Marion58fd4812022-03-14 13:04:38 +0100208 vlib_stats_entry_t e = { .type = t };
Damjan Marion8973b072022-03-01 15:51:18 +0100209
210 ASSERT (shared_header);
211
Damjan Marion58fd4812022-03-14 13:04:38 +0100212 u32 vector_index = vlib_stats_find_entry_index ("%v", name);
213 if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
Damjan Marion8973b072022-03-01 15:51:18 +0100214 {
Damjan Marion58fd4812022-03-14 13:04:38 +0100215 vector_index = ~0;
216 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100217 }
218
Damjan Marion58fd4812022-03-14 13:04:38 +0100219 vec_add1 (name, 0);
220 vlib_stats_set_entry_name (&e, (char *) name);
Damjan Marion8973b072022-03-01 15:51:18 +0100221
Damjan Marion58fd4812022-03-14 13:04:38 +0100222 vlib_stats_segment_lock ();
223 vector_index = vlib_stats_create_counter (&e);
224
Damjan Marion8973b072022-03-01 15:51:18 +0100225 shared_header->directory_vector = sm->directory_vector;
226
227 vlib_stats_segment_unlock ();
Damjan Marion58fd4812022-03-14 13:04:38 +0100228
229done:
230 vec_free (name);
231 return vector_index;
232}
233
234u32
235vlib_stats_add_gauge (char *fmt, ...)
236{
237 va_list va;
238 u8 *name;
239
240 va_start (va, fmt);
241 name = va_format (0, fmt, &va);
242 va_end (va);
243 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
Damjan Marion8973b072022-03-01 15:51:18 +0100244}
245
246void
247vlib_stats_register_error_index (u64 *em_vec, u64 index, char *fmt, ...)
248{
249 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
250 vlib_stats_shared_header_t *shared_header = sm->shared_header;
251 vlib_stats_entry_t e = {};
252 va_list va;
253 u8 *name;
254
255 va_start (va, fmt);
256 name = va_format (0, fmt, &va);
257 va_end (va);
258
259 ASSERT (shared_header);
260
261 vlib_stats_segment_lock ();
262 u32 vector_index = vlib_stats_find_entry_index ("%v", name);
263
264 if (vector_index == STAT_SEGMENT_INDEX_INVALID)
265 {
266 vec_add1 (name, 0);
267 vlib_stats_set_entry_name (&e, (char *) name);
268 e.type = STAT_DIR_TYPE_ERROR_INDEX;
269 e.index = index;
270 vector_index = vlib_stats_create_counter (&e);
271
272 /* Warn clients to refresh any pointers they might be holding */
273 shared_header->directory_vector = sm->directory_vector;
274 }
275
276 vlib_stats_segment_unlock ();
277 vec_free (name);
278}
279
280void
281vlib_stats_update_error_vector (u64 *error_vector, u32 thread_index, int lock)
282{
283 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
284 vlib_stats_shared_header_t *shared_header = sm->shared_header;
285 void *oldheap = clib_mem_set_heap (sm->heap);
286
287 ASSERT (shared_header);
288
289 if (lock)
290 vlib_stats_segment_lock ();
291
292 /* Reset the client hash table pointer, since it WILL change! */
293 vec_validate (sm->error_vector, thread_index);
294 sm->error_vector[thread_index] = error_vector;
295
296 shared_header->error_vector = sm->error_vector;
297 shared_header->directory_vector = sm->directory_vector;
298
299 if (lock)
300 vlib_stats_segment_unlock ();
301 clib_mem_set_heap (oldheap);
302}
303
304void
Damjan Marion8973b072022-03-01 15:51:18 +0100305vlib_stats_set_gauge (u32 index, u64 value)
306{
307 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
308
309 ASSERT (index < vec_len (sm->directory_vector));
310 sm->directory_vector[index].value = value;
311}
312
313u32
314vlib_stats_add_timestamp (char *fmt, ...)
315{
316 va_list va;
317 u8 *name;
318
319 va_start (va, fmt);
320 name = va_format (0, fmt, &va);
321 va_end (va);
322 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
323}
324
325void
326vlib_stats_set_timestamp (u32 entry_index, f64 value)
327{
328 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
329
330 ASSERT (entry_index < vec_len (sm->directory_vector));
331 sm->directory_vector[entry_index].value = value;
332}
333
334u32
335vlib_stats_add_string_vector (char *fmt, ...)
336{
337 va_list va;
338 u8 *name;
339
340 va_start (va, fmt);
341 name = va_format (0, fmt, &va);
342 va_end (va);
343 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
344}
345
346void
347vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
348 ...)
349{
350 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
351 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
352 va_list va;
353 void *oldheap;
354
355 oldheap = clib_mem_set_heap (sm->heap);
356 vlib_stats_segment_lock ();
357
358 vec_validate (e->string_vector, vector_index);
359 vec_reset_length (e->string_vector[vector_index]);
360
361 va_start (va, fmt);
362 e->string_vector[vector_index] =
363 va_format (e->string_vector[vector_index], fmt, &va);
364 va_end (va);
365
366 vlib_stats_segment_unlock ();
367 clib_mem_set_heap (oldheap);
368}
369
370u32
371vlib_stats_add_counter_vector (char *fmt, ...)
372{
373 va_list va;
374 u8 *name;
375
376 va_start (va, fmt);
377 name = va_format (0, fmt, &va);
378 va_end (va);
379 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
380 name);
381}
382
Damjan Marion58fd4812022-03-14 13:04:38 +0100383u32
384vlib_stats_add_counter_pair_vector (char *fmt, ...)
385{
386 va_list va;
387 u8 *name;
388
389 va_start (va, fmt);
390 name = va_format (0, fmt, &va);
391 va_end (va);
392 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
393 name);
394}
395
396static int
397vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
Damjan Marion8973b072022-03-01 15:51:18 +0100398{
399 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
400 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
401 void *oldheap;
Damjan Marion58fd4812022-03-14 13:04:38 +0100402 int rv = 1;
Damjan Marion8973b072022-03-01 15:51:18 +0100403
404 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100405 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
406 {
407 u32 idx0 = va_arg (*va, u32);
408 u32 idx1 = va_arg (*va, u32);
409 u64 **data = e->data;
Damjan Marion8973b072022-03-01 15:51:18 +0100410
Damjan Marion58fd4812022-03-14 13:04:38 +0100411 if (idx0 >= vec_max_len (data))
412 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100413
Damjan Marion58fd4812022-03-14 13:04:38 +0100414 for (u32 i = 0; i <= idx0; i++)
415 if (idx1 >= vec_max_len (data[idx0]))
416 goto done;
417 }
418 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
419 {
420 u32 idx0 = va_arg (*va, u32);
421 u32 idx1 = va_arg (*va, u32);
422 vlib_counter_t **data = e->data;
423
424 va_end (*va);
425
426 if (idx0 >= vec_max_len (data))
427 goto done;
428
429 for (u32 i = 0; i <= idx0; i++)
430 if (idx1 >= vec_max_len (data[idx0]))
431 goto done;
432 }
433 else
434 ASSERT (0);
435
436 rv = 0;
437done:
Damjan Marion8973b072022-03-01 15:51:18 +0100438 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100439 return rv;
440}
441
442int
443vlib_stats_validate_will_expand (u32 entry_index, ...)
444{
445 va_list va;
446 int rv;
447
448 va_start (va, entry_index);
449 rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
450 va_end (va);
451 return rv;
452}
453
454void
455vlib_stats_validate (u32 entry_index, ...)
456{
457 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
458 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
459 void *oldheap;
460 va_list va;
461 int will_expand;
462
463 va_start (va, entry_index);
464 will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
465 va_end (va);
466
467 if (will_expand)
468 vlib_stats_segment_lock ();
469
470 oldheap = clib_mem_set_heap (sm->heap);
471
472 va_start (va, entry_index);
473
474 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
475 {
476 u32 idx0 = va_arg (va, u32);
477 u32 idx1 = va_arg (va, u32);
478 u64 **data = e->data;
479
480 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
481
482 for (u32 i = 0; i <= idx0; i++)
483 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
484 e->data = data;
485 }
486 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
487 {
488 u32 idx0 = va_arg (va, u32);
489 u32 idx1 = va_arg (va, u32);
490 vlib_counter_t **data = e->data;
491
492 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
493
494 for (u32 i = 0; i <= idx0; i++)
495 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
496 e->data = data;
497 }
498 else
499 ASSERT (0);
500
501 va_end (va);
502
503 clib_mem_set_heap (oldheap);
504
505 if (will_expand)
506 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100507}
508
509u32
510vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
511{
512 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
513 vlib_stats_shared_header_t *shared_header = sm->shared_header;
514 vlib_stats_entry_t e;
515 va_list va;
516 u8 *name;
517
518 ASSERT (shared_header);
519 ASSERT (entry_index < vec_len (sm->directory_vector));
520
521 va_start (va, fmt);
522 name = va_format (0, fmt, &va);
523 va_end (va);
524
525 if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
526 {
527 vec_add1 (name, 0);
528 vlib_stats_set_entry_name (&e, (char *) name);
529 e.type = STAT_DIR_TYPE_SYMLINK;
530 e.index1 = entry_index;
531 e.index2 = vector_index;
532 vector_index = vlib_stats_create_counter (&e);
533
534 /* Warn clients to refresh any pointers they might be holding */
535 shared_header->directory_vector = sm->directory_vector;
536 }
537 else
538 vector_index = ~0;
539
540 vec_free (name);
541 return vector_index;
542}
543
544void
545vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
546{
547 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
548 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
549 va_list va;
550 u8 *new_name;
551
552 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
553
554 va_start (va, fmt);
555 new_name = va_format (0, fmt, &va);
556 va_end (va);
557
558 vec_add1 (new_name, 0);
559 vlib_stats_set_entry_name (e, (char *) new_name);
560 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
561 vec_free (new_name);
562}
563
564f64
565vlib_stats_get_segment_update_rate (void)
566{
567 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
568 return sm->update_interval;
569}
570
571void
572vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
573{
574 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
575 vlib_stats_collector_t *c;
576
577 ASSERT (reg->entry_index != ~0);
578
579 pool_get_zero (sm->collectors, c);
580 c->fn = reg->collect_fn;
581 c->entry_index = reg->entry_index;
582 c->vector_index = reg->vector_index;
583 c->private_data = reg->private_data;
584
585 return;
586}