blob: a19909d0dc48d2b50390f7016ac2cb37c0fd3fd3 [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;
Damjan Marioneecec8c2022-04-06 12:06:41 +0200109 u32 index;
Damjan Marion8973b072022-03-01 15:51:18 +0100110
111 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion8973b072022-03-01 15:51:18 +0100112
Damjan Marioneecec8c2022-04-06 12:06:41 +0200113 if (sm->dir_vector_first_free_elt != CLIB_U32_MAX)
114 {
115 index = sm->dir_vector_first_free_elt;
116 sm->dir_vector_first_free_elt = sm->directory_vector[index].index;
117 }
118 else
119 {
120 index = vec_len (sm->directory_vector);
121 vec_validate (sm->directory_vector, index);
122 }
Damjan Marion8973b072022-03-01 15:51:18 +0100123
Damjan Marion8973b072022-03-01 15:51:18 +0100124 sm->directory_vector[index] = *e;
125
126 clib_mem_set_heap (oldheap);
127 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
128
129 return index;
130}
131
132void
133vlib_stats_remove_entry (u32 entry_index)
134{
135 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
136 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
137 void *oldheap;
138 counter_t **c;
Damjan Marion58fd4812022-03-14 13:04:38 +0100139 vlib_counter_t **vc;
Damjan Marion8973b072022-03-01 15:51:18 +0100140 u32 i;
141
142 if (entry_index >= vec_len (sm->directory_vector))
143 return;
144
145 oldheap = clib_mem_set_heap (sm->heap);
146
Damjan Marion58fd4812022-03-14 13:04:38 +0100147 vlib_stats_segment_lock ();
148
Damjan Marion8973b072022-03-01 15:51:18 +0100149 switch (e->type)
150 {
151 case STAT_DIR_TYPE_NAME_VECTOR:
152 for (i = 0; i < vec_len (e->string_vector); i++)
153 vec_free (e->string_vector[i]);
154 vec_free (e->string_vector);
155 break;
156
157 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
158 c = e->data;
159 e->data = 0;
160 for (i = 0; i < vec_len (c); i++)
161 vec_free (c[i]);
162 vec_free (c);
163 break;
164
Damjan Marion58fd4812022-03-14 13:04:38 +0100165 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
166 vc = e->data;
167 e->data = 0;
168 for (i = 0; i < vec_len (vc); i++)
169 vec_free (vc[i]);
170 vec_free (vc);
171 break;
172
Damjan Marion8973b072022-03-01 15:51:18 +0100173 case STAT_DIR_TYPE_SCALAR_INDEX:
174 case STAT_DIR_TYPE_SYMLINK:
175 break;
176 default:
177 ASSERT (0);
178 }
179
Damjan Marion58fd4812022-03-14 13:04:38 +0100180 vlib_stats_segment_unlock ();
181
Damjan Marion8973b072022-03-01 15:51:18 +0100182 clib_mem_set_heap (oldheap);
183 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
184
185 memset (e, 0, sizeof (*e));
186 e->type = STAT_DIR_TYPE_EMPTY;
Damjan Marioneecec8c2022-04-06 12:06:41 +0200187
188 e->value = sm->dir_vector_first_free_elt;
189 sm->dir_vector_first_free_elt = entry_index;
Damjan Marion8973b072022-03-01 15:51:18 +0100190}
191
192static void
193vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
194{
195 u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
196
197 for (i = 0; i < len; i++)
198 {
199 e->name[i] = s[i];
200 if (s[i] == 0)
201 return;
202 }
203 ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
204 s[i] = 0;
205}
206
Damjan Marion58fd4812022-03-14 13:04:38 +0100207static u32
208vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
Damjan Marion8973b072022-03-01 15:51:18 +0100209{
Damjan Marion8973b072022-03-01 15:51:18 +0100210 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
211 vlib_stats_shared_header_t *shared_header = sm->shared_header;
Damjan Marion58fd4812022-03-14 13:04:38 +0100212 vlib_stats_entry_t e = { .type = t };
Damjan Marion8973b072022-03-01 15:51:18 +0100213
214 ASSERT (shared_header);
215
Damjan Marion58fd4812022-03-14 13:04:38 +0100216 u32 vector_index = vlib_stats_find_entry_index ("%v", name);
217 if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
Damjan Marion8973b072022-03-01 15:51:18 +0100218 {
Damjan Marion58fd4812022-03-14 13:04:38 +0100219 vector_index = ~0;
220 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100221 }
222
Damjan Marion58fd4812022-03-14 13:04:38 +0100223 vec_add1 (name, 0);
224 vlib_stats_set_entry_name (&e, (char *) name);
Damjan Marion8973b072022-03-01 15:51:18 +0100225
Damjan Marion58fd4812022-03-14 13:04:38 +0100226 vlib_stats_segment_lock ();
227 vector_index = vlib_stats_create_counter (&e);
228
Damjan Marion8973b072022-03-01 15:51:18 +0100229 shared_header->directory_vector = sm->directory_vector;
230
231 vlib_stats_segment_unlock ();
Damjan Marion58fd4812022-03-14 13:04:38 +0100232
233done:
234 vec_free (name);
235 return vector_index;
236}
237
238u32
239vlib_stats_add_gauge (char *fmt, ...)
240{
241 va_list va;
242 u8 *name;
243
244 va_start (va, fmt);
245 name = va_format (0, fmt, &va);
246 va_end (va);
247 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
Damjan Marion8973b072022-03-01 15:51:18 +0100248}
249
250void
Damjan Marion8973b072022-03-01 15:51:18 +0100251vlib_stats_set_gauge (u32 index, u64 value)
252{
253 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
254
255 ASSERT (index < vec_len (sm->directory_vector));
256 sm->directory_vector[index].value = value;
257}
258
259u32
260vlib_stats_add_timestamp (char *fmt, ...)
261{
262 va_list va;
263 u8 *name;
264
265 va_start (va, fmt);
266 name = va_format (0, fmt, &va);
267 va_end (va);
268 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
269}
270
271void
272vlib_stats_set_timestamp (u32 entry_index, f64 value)
273{
274 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
275
276 ASSERT (entry_index < vec_len (sm->directory_vector));
277 sm->directory_vector[entry_index].value = value;
278}
279
280u32
281vlib_stats_add_string_vector (char *fmt, ...)
282{
283 va_list va;
284 u8 *name;
285
286 va_start (va, fmt);
287 name = va_format (0, fmt, &va);
288 va_end (va);
289 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
290}
291
292void
293vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
294 ...)
295{
296 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
297 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
298 va_list va;
299 void *oldheap;
300
301 oldheap = clib_mem_set_heap (sm->heap);
302 vlib_stats_segment_lock ();
303
304 vec_validate (e->string_vector, vector_index);
305 vec_reset_length (e->string_vector[vector_index]);
306
307 va_start (va, fmt);
308 e->string_vector[vector_index] =
309 va_format (e->string_vector[vector_index], fmt, &va);
310 va_end (va);
311
312 vlib_stats_segment_unlock ();
313 clib_mem_set_heap (oldheap);
314}
315
316u32
317vlib_stats_add_counter_vector (char *fmt, ...)
318{
319 va_list va;
320 u8 *name;
321
322 va_start (va, fmt);
323 name = va_format (0, fmt, &va);
324 va_end (va);
325 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
326 name);
327}
328
Damjan Marion58fd4812022-03-14 13:04:38 +0100329u32
330vlib_stats_add_counter_pair_vector (char *fmt, ...)
331{
332 va_list va;
333 u8 *name;
334
335 va_start (va, fmt);
336 name = va_format (0, fmt, &va);
337 va_end (va);
338 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
339 name);
340}
341
342static int
343vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
Damjan Marion8973b072022-03-01 15:51:18 +0100344{
345 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
346 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
347 void *oldheap;
Damjan Marion58fd4812022-03-14 13:04:38 +0100348 int rv = 1;
Damjan Marion8973b072022-03-01 15:51:18 +0100349
350 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100351 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
352 {
353 u32 idx0 = va_arg (*va, u32);
354 u32 idx1 = va_arg (*va, u32);
355 u64 **data = e->data;
Damjan Marion8973b072022-03-01 15:51:18 +0100356
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000357 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100358 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100359
Damjan Marion58fd4812022-03-14 13:04:38 +0100360 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100361 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100362 goto done;
363 }
364 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
365 {
366 u32 idx0 = va_arg (*va, u32);
367 u32 idx1 = va_arg (*va, u32);
368 vlib_counter_t **data = e->data;
369
370 va_end (*va);
371
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000372 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100373 goto done;
374
375 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100376 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100377 goto done;
378 }
379 else
380 ASSERT (0);
381
382 rv = 0;
383done:
Damjan Marion8973b072022-03-01 15:51:18 +0100384 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100385 return rv;
386}
387
388int
389vlib_stats_validate_will_expand (u32 entry_index, ...)
390{
391 va_list va;
392 int rv;
393
394 va_start (va, entry_index);
395 rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
396 va_end (va);
397 return rv;
398}
399
400void
401vlib_stats_validate (u32 entry_index, ...)
402{
403 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
404 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
405 void *oldheap;
406 va_list va;
407 int will_expand;
408
409 va_start (va, entry_index);
410 will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
411 va_end (va);
412
413 if (will_expand)
414 vlib_stats_segment_lock ();
415
416 oldheap = clib_mem_set_heap (sm->heap);
417
418 va_start (va, entry_index);
419
420 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
421 {
422 u32 idx0 = va_arg (va, u32);
423 u32 idx1 = va_arg (va, u32);
424 u64 **data = e->data;
425
426 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
427
428 for (u32 i = 0; i <= idx0; i++)
429 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
430 e->data = data;
431 }
432 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
433 {
434 u32 idx0 = va_arg (va, u32);
435 u32 idx1 = va_arg (va, u32);
436 vlib_counter_t **data = e->data;
437
438 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
439
440 for (u32 i = 0; i <= idx0; i++)
441 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
442 e->data = data;
443 }
444 else
445 ASSERT (0);
446
447 va_end (va);
448
449 clib_mem_set_heap (oldheap);
450
451 if (will_expand)
452 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100453}
454
455u32
456vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
457{
458 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
459 vlib_stats_shared_header_t *shared_header = sm->shared_header;
460 vlib_stats_entry_t e;
461 va_list va;
462 u8 *name;
463
464 ASSERT (shared_header);
465 ASSERT (entry_index < vec_len (sm->directory_vector));
466
467 va_start (va, fmt);
468 name = va_format (0, fmt, &va);
469 va_end (va);
470
471 if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
472 {
473 vec_add1 (name, 0);
474 vlib_stats_set_entry_name (&e, (char *) name);
475 e.type = STAT_DIR_TYPE_SYMLINK;
476 e.index1 = entry_index;
477 e.index2 = vector_index;
478 vector_index = vlib_stats_create_counter (&e);
479
480 /* Warn clients to refresh any pointers they might be holding */
481 shared_header->directory_vector = sm->directory_vector;
482 }
483 else
484 vector_index = ~0;
485
486 vec_free (name);
487 return vector_index;
488}
489
490void
491vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
492{
493 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
494 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
495 va_list va;
496 u8 *new_name;
497
498 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
499
500 va_start (va, fmt);
501 new_name = va_format (0, fmt, &va);
502 va_end (va);
503
504 vec_add1 (new_name, 0);
505 vlib_stats_set_entry_name (e, (char *) new_name);
506 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
507 vec_free (new_name);
508}
509
510f64
511vlib_stats_get_segment_update_rate (void)
512{
513 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
514 return sm->update_interval;
515}
516
517void
518vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
519{
520 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
521 vlib_stats_collector_t *c;
522
523 ASSERT (reg->entry_index != ~0);
524
525 pool_get_zero (sm->collectors, c);
526 c->fn = reg->collect_fn;
527 c->entry_index = reg->entry_index;
528 c->vector_index = reg->vector_index;
529 c->private_data = reg->private_data;
530
531 return;
532}