blob: 61dadd74b1c448921c49204d44c664e5df324a7c [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 ();
Damjan Marioneecec8c2022-04-06 12:06:41 +0200108 u32 index;
Damjan Marion8973b072022-03-01 15:51:18 +0100109
Damjan Marioneecec8c2022-04-06 12:06:41 +0200110 if (sm->dir_vector_first_free_elt != CLIB_U32_MAX)
111 {
112 index = sm->dir_vector_first_free_elt;
113 sm->dir_vector_first_free_elt = sm->directory_vector[index].index;
114 }
115 else
116 {
117 index = vec_len (sm->directory_vector);
118 vec_validate (sm->directory_vector, index);
119 }
Damjan Marion8973b072022-03-01 15:51:18 +0100120
Damjan Marion8973b072022-03-01 15:51:18 +0100121 sm->directory_vector[index] = *e;
122
Damjan Marion8973b072022-03-01 15:51:18 +0100123 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
124
125 return index;
126}
127
128void
129vlib_stats_remove_entry (u32 entry_index)
130{
131 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
132 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
Damjan Marion8973b072022-03-01 15:51:18 +0100133 counter_t **c;
Damjan Marion58fd4812022-03-14 13:04:38 +0100134 vlib_counter_t **vc;
Damjan Marion8973b072022-03-01 15:51:18 +0100135 u32 i;
136
137 if (entry_index >= vec_len (sm->directory_vector))
138 return;
139
Damjan Marion58fd4812022-03-14 13:04:38 +0100140 vlib_stats_segment_lock ();
141
Damjan Marion8973b072022-03-01 15:51:18 +0100142 switch (e->type)
143 {
144 case STAT_DIR_TYPE_NAME_VECTOR:
145 for (i = 0; i < vec_len (e->string_vector); i++)
146 vec_free (e->string_vector[i]);
147 vec_free (e->string_vector);
148 break;
149
150 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
151 c = e->data;
152 e->data = 0;
153 for (i = 0; i < vec_len (c); i++)
154 vec_free (c[i]);
155 vec_free (c);
156 break;
157
Damjan Marion58fd4812022-03-14 13:04:38 +0100158 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
159 vc = e->data;
160 e->data = 0;
161 for (i = 0; i < vec_len (vc); i++)
162 vec_free (vc[i]);
163 vec_free (vc);
164 break;
165
Damjan Marion8973b072022-03-01 15:51:18 +0100166 case STAT_DIR_TYPE_SCALAR_INDEX:
167 case STAT_DIR_TYPE_SYMLINK:
168 break;
169 default:
170 ASSERT (0);
171 }
172
Damjan Marion58fd4812022-03-14 13:04:38 +0100173 vlib_stats_segment_unlock ();
174
Damjan Marion8973b072022-03-01 15:51:18 +0100175 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
176
177 memset (e, 0, sizeof (*e));
178 e->type = STAT_DIR_TYPE_EMPTY;
Damjan Marioneecec8c2022-04-06 12:06:41 +0200179
180 e->value = sm->dir_vector_first_free_elt;
181 sm->dir_vector_first_free_elt = entry_index;
Damjan Marion8973b072022-03-01 15:51:18 +0100182}
183
184static void
185vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
186{
187 u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
188
189 for (i = 0; i < len; i++)
190 {
191 e->name[i] = s[i];
192 if (s[i] == 0)
193 return;
194 }
195 ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
196 s[i] = 0;
197}
198
Damjan Marion58fd4812022-03-14 13:04:38 +0100199static u32
200vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
Damjan Marion8973b072022-03-01 15:51:18 +0100201{
Damjan Marion8973b072022-03-01 15:51:18 +0100202 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
203 vlib_stats_shared_header_t *shared_header = sm->shared_header;
Damjan Marion58fd4812022-03-14 13:04:38 +0100204 vlib_stats_entry_t e = { .type = t };
Damjan Marion8973b072022-03-01 15:51:18 +0100205
206 ASSERT (shared_header);
207
Damjan Marion58fd4812022-03-14 13:04:38 +0100208 u32 vector_index = vlib_stats_find_entry_index ("%v", name);
209 if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
Damjan Marion8973b072022-03-01 15:51:18 +0100210 {
Damjan Marion58fd4812022-03-14 13:04:38 +0100211 vector_index = ~0;
212 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100213 }
214
Damjan Marion58fd4812022-03-14 13:04:38 +0100215 vec_add1 (name, 0);
216 vlib_stats_set_entry_name (&e, (char *) name);
Damjan Marion8973b072022-03-01 15:51:18 +0100217
Damjan Marion58fd4812022-03-14 13:04:38 +0100218 vlib_stats_segment_lock ();
219 vector_index = vlib_stats_create_counter (&e);
220
Damjan Marion8973b072022-03-01 15:51:18 +0100221 shared_header->directory_vector = sm->directory_vector;
222
223 vlib_stats_segment_unlock ();
Damjan Marion58fd4812022-03-14 13:04:38 +0100224
225done:
226 vec_free (name);
227 return vector_index;
228}
229
230u32
231vlib_stats_add_gauge (char *fmt, ...)
232{
233 va_list va;
234 u8 *name;
235
236 va_start (va, fmt);
237 name = va_format (0, fmt, &va);
238 va_end (va);
239 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
Damjan Marion8973b072022-03-01 15:51:18 +0100240}
241
242void
Damjan Marion8973b072022-03-01 15:51:18 +0100243vlib_stats_set_gauge (u32 index, u64 value)
244{
245 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
246
247 ASSERT (index < vec_len (sm->directory_vector));
248 sm->directory_vector[index].value = value;
249}
250
251u32
252vlib_stats_add_timestamp (char *fmt, ...)
253{
254 va_list va;
255 u8 *name;
256
257 va_start (va, fmt);
258 name = va_format (0, fmt, &va);
259 va_end (va);
260 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
261}
262
263void
264vlib_stats_set_timestamp (u32 entry_index, f64 value)
265{
266 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
267
268 ASSERT (entry_index < vec_len (sm->directory_vector));
269 sm->directory_vector[entry_index].value = value;
270}
271
272u32
273vlib_stats_add_string_vector (char *fmt, ...)
274{
275 va_list va;
276 u8 *name;
277
278 va_start (va, fmt);
279 name = va_format (0, fmt, &va);
280 va_end (va);
281 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
282}
283
284void
285vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
286 ...)
287{
288 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
289 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
290 va_list va;
291 void *oldheap;
292
293 oldheap = clib_mem_set_heap (sm->heap);
294 vlib_stats_segment_lock ();
295
296 vec_validate (e->string_vector, vector_index);
297 vec_reset_length (e->string_vector[vector_index]);
298
299 va_start (va, fmt);
300 e->string_vector[vector_index] =
301 va_format (e->string_vector[vector_index], fmt, &va);
302 va_end (va);
303
304 vlib_stats_segment_unlock ();
305 clib_mem_set_heap (oldheap);
306}
307
308u32
309vlib_stats_add_counter_vector (char *fmt, ...)
310{
311 va_list va;
312 u8 *name;
313
314 va_start (va, fmt);
315 name = va_format (0, fmt, &va);
316 va_end (va);
317 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
318 name);
319}
320
Damjan Marion58fd4812022-03-14 13:04:38 +0100321u32
322vlib_stats_add_counter_pair_vector (char *fmt, ...)
323{
324 va_list va;
325 u8 *name;
326
327 va_start (va, fmt);
328 name = va_format (0, fmt, &va);
329 va_end (va);
330 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
331 name);
332}
333
334static int
335vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
Damjan Marion8973b072022-03-01 15:51:18 +0100336{
337 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
338 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
339 void *oldheap;
Damjan Marion58fd4812022-03-14 13:04:38 +0100340 int rv = 1;
Damjan Marion8973b072022-03-01 15:51:18 +0100341
342 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100343 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
344 {
345 u32 idx0 = va_arg (*va, u32);
346 u32 idx1 = va_arg (*va, u32);
347 u64 **data = e->data;
Damjan Marion8973b072022-03-01 15:51:18 +0100348
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000349 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100350 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100351
Damjan Marion58fd4812022-03-14 13:04:38 +0100352 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100353 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100354 goto done;
355 }
356 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
357 {
358 u32 idx0 = va_arg (*va, u32);
359 u32 idx1 = va_arg (*va, u32);
360 vlib_counter_t **data = e->data;
361
362 va_end (*va);
363
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000364 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100365 goto done;
366
367 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100368 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100369 goto done;
370 }
371 else
372 ASSERT (0);
373
374 rv = 0;
375done:
Damjan Marion8973b072022-03-01 15:51:18 +0100376 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100377 return rv;
378}
379
380int
381vlib_stats_validate_will_expand (u32 entry_index, ...)
382{
383 va_list va;
384 int rv;
385
386 va_start (va, entry_index);
387 rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
388 va_end (va);
389 return rv;
390}
391
392void
393vlib_stats_validate (u32 entry_index, ...)
394{
395 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
396 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
397 void *oldheap;
398 va_list va;
399 int will_expand;
400
401 va_start (va, entry_index);
402 will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
403 va_end (va);
404
405 if (will_expand)
406 vlib_stats_segment_lock ();
407
408 oldheap = clib_mem_set_heap (sm->heap);
409
410 va_start (va, entry_index);
411
412 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
413 {
414 u32 idx0 = va_arg (va, u32);
415 u32 idx1 = va_arg (va, u32);
416 u64 **data = e->data;
417
418 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
419
420 for (u32 i = 0; i <= idx0; i++)
421 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
422 e->data = data;
423 }
424 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
425 {
426 u32 idx0 = va_arg (va, u32);
427 u32 idx1 = va_arg (va, u32);
428 vlib_counter_t **data = e->data;
429
430 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
431
432 for (u32 i = 0; i <= idx0; i++)
433 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
434 e->data = data;
435 }
436 else
437 ASSERT (0);
438
439 va_end (va);
440
441 clib_mem_set_heap (oldheap);
442
443 if (will_expand)
444 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100445}
446
447u32
448vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
449{
450 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
451 vlib_stats_shared_header_t *shared_header = sm->shared_header;
452 vlib_stats_entry_t e;
453 va_list va;
454 u8 *name;
455
456 ASSERT (shared_header);
457 ASSERT (entry_index < vec_len (sm->directory_vector));
458
459 va_start (va, fmt);
460 name = va_format (0, fmt, &va);
461 va_end (va);
462
463 if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
464 {
465 vec_add1 (name, 0);
466 vlib_stats_set_entry_name (&e, (char *) name);
467 e.type = STAT_DIR_TYPE_SYMLINK;
468 e.index1 = entry_index;
469 e.index2 = vector_index;
470 vector_index = vlib_stats_create_counter (&e);
471
472 /* Warn clients to refresh any pointers they might be holding */
473 shared_header->directory_vector = sm->directory_vector;
474 }
475 else
476 vector_index = ~0;
477
478 vec_free (name);
479 return vector_index;
480}
481
482void
483vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
484{
485 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
486 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
487 va_list va;
488 u8 *new_name;
489
490 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
491
492 va_start (va, fmt);
493 new_name = va_format (0, fmt, &va);
494 va_end (va);
495
496 vec_add1 (new_name, 0);
497 vlib_stats_set_entry_name (e, (char *) new_name);
498 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
499 vec_free (new_name);
500}
501
502f64
503vlib_stats_get_segment_update_rate (void)
504{
505 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
506 return sm->update_interval;
507}
508
509void
510vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
511{
512 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
513 vlib_stats_collector_t *c;
514
515 ASSERT (reg->entry_index != ~0);
516
517 pool_get_zero (sm->collectors, c);
518 c->fn = reg->collect_fn;
519 c->entry_index = reg->entry_index;
520 c->vector_index = reg->vector_index;
521 c->private_data = reg->private_data;
522
523 return;
524}