blob: 9063fa375e657175400f03c5742389e500e8094c [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
Damjan Marion8973b072022-03-01 15:51:18 +0100247vlib_stats_set_gauge (u32 index, u64 value)
248{
249 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
250
251 ASSERT (index < vec_len (sm->directory_vector));
252 sm->directory_vector[index].value = value;
253}
254
255u32
256vlib_stats_add_timestamp (char *fmt, ...)
257{
258 va_list va;
259 u8 *name;
260
261 va_start (va, fmt);
262 name = va_format (0, fmt, &va);
263 va_end (va);
264 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
265}
266
267void
268vlib_stats_set_timestamp (u32 entry_index, f64 value)
269{
270 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
271
272 ASSERT (entry_index < vec_len (sm->directory_vector));
273 sm->directory_vector[entry_index].value = value;
274}
275
276u32
277vlib_stats_add_string_vector (char *fmt, ...)
278{
279 va_list va;
280 u8 *name;
281
282 va_start (va, fmt);
283 name = va_format (0, fmt, &va);
284 va_end (va);
285 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
286}
287
288void
289vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
290 ...)
291{
292 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
293 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
294 va_list va;
295 void *oldheap;
296
297 oldheap = clib_mem_set_heap (sm->heap);
298 vlib_stats_segment_lock ();
299
300 vec_validate (e->string_vector, vector_index);
301 vec_reset_length (e->string_vector[vector_index]);
302
303 va_start (va, fmt);
304 e->string_vector[vector_index] =
305 va_format (e->string_vector[vector_index], fmt, &va);
306 va_end (va);
307
308 vlib_stats_segment_unlock ();
309 clib_mem_set_heap (oldheap);
310}
311
312u32
313vlib_stats_add_counter_vector (char *fmt, ...)
314{
315 va_list va;
316 u8 *name;
317
318 va_start (va, fmt);
319 name = va_format (0, fmt, &va);
320 va_end (va);
321 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
322 name);
323}
324
Damjan Marion58fd4812022-03-14 13:04:38 +0100325u32
326vlib_stats_add_counter_pair_vector (char *fmt, ...)
327{
328 va_list va;
329 u8 *name;
330
331 va_start (va, fmt);
332 name = va_format (0, fmt, &va);
333 va_end (va);
334 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
335 name);
336}
337
338static int
339vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
Damjan Marion8973b072022-03-01 15:51:18 +0100340{
341 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
342 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
343 void *oldheap;
Damjan Marion58fd4812022-03-14 13:04:38 +0100344 int rv = 1;
Damjan Marion8973b072022-03-01 15:51:18 +0100345
346 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100347 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
348 {
349 u32 idx0 = va_arg (*va, u32);
350 u32 idx1 = va_arg (*va, u32);
351 u64 **data = e->data;
Damjan Marion8973b072022-03-01 15:51:18 +0100352
Damjan Marion58fd4812022-03-14 13:04:38 +0100353 if (idx0 >= vec_max_len (data))
354 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100355
Damjan Marion58fd4812022-03-14 13:04:38 +0100356 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100357 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100358 goto done;
359 }
360 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
361 {
362 u32 idx0 = va_arg (*va, u32);
363 u32 idx1 = va_arg (*va, u32);
364 vlib_counter_t **data = e->data;
365
366 va_end (*va);
367
368 if (idx0 >= vec_max_len (data))
369 goto done;
370
371 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100372 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100373 goto done;
374 }
375 else
376 ASSERT (0);
377
378 rv = 0;
379done:
Damjan Marion8973b072022-03-01 15:51:18 +0100380 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100381 return rv;
382}
383
384int
385vlib_stats_validate_will_expand (u32 entry_index, ...)
386{
387 va_list va;
388 int rv;
389
390 va_start (va, entry_index);
391 rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
392 va_end (va);
393 return rv;
394}
395
396void
397vlib_stats_validate (u32 entry_index, ...)
398{
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;
402 va_list va;
403 int will_expand;
404
405 va_start (va, entry_index);
406 will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
407 va_end (va);
408
409 if (will_expand)
410 vlib_stats_segment_lock ();
411
412 oldheap = clib_mem_set_heap (sm->heap);
413
414 va_start (va, entry_index);
415
416 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
417 {
418 u32 idx0 = va_arg (va, u32);
419 u32 idx1 = va_arg (va, u32);
420 u64 **data = e->data;
421
422 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
423
424 for (u32 i = 0; i <= idx0; i++)
425 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
426 e->data = data;
427 }
428 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
429 {
430 u32 idx0 = va_arg (va, u32);
431 u32 idx1 = va_arg (va, u32);
432 vlib_counter_t **data = e->data;
433
434 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
435
436 for (u32 i = 0; i <= idx0; i++)
437 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
438 e->data = data;
439 }
440 else
441 ASSERT (0);
442
443 va_end (va);
444
445 clib_mem_set_heap (oldheap);
446
447 if (will_expand)
448 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100449}
450
451u32
452vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
453{
454 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
455 vlib_stats_shared_header_t *shared_header = sm->shared_header;
456 vlib_stats_entry_t e;
457 va_list va;
458 u8 *name;
459
460 ASSERT (shared_header);
461 ASSERT (entry_index < vec_len (sm->directory_vector));
462
463 va_start (va, fmt);
464 name = va_format (0, fmt, &va);
465 va_end (va);
466
467 if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
468 {
469 vec_add1 (name, 0);
470 vlib_stats_set_entry_name (&e, (char *) name);
471 e.type = STAT_DIR_TYPE_SYMLINK;
472 e.index1 = entry_index;
473 e.index2 = vector_index;
474 vector_index = vlib_stats_create_counter (&e);
475
476 /* Warn clients to refresh any pointers they might be holding */
477 shared_header->directory_vector = sm->directory_vector;
478 }
479 else
480 vector_index = ~0;
481
482 vec_free (name);
483 return vector_index;
484}
485
486void
487vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
488{
489 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
490 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
491 va_list va;
492 u8 *new_name;
493
494 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
495
496 va_start (va, fmt);
497 new_name = va_format (0, fmt, &va);
498 va_end (va);
499
500 vec_add1 (new_name, 0);
501 vlib_stats_set_entry_name (e, (char *) new_name);
502 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
503 vec_free (new_name);
504}
505
506f64
507vlib_stats_get_segment_update_rate (void)
508{
509 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
510 return sm->update_interval;
511}
512
513void
514vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
515{
516 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
517 vlib_stats_collector_t *c;
518
519 ASSERT (reg->entry_index != ~0);
520
521 pool_get_zero (sm->collectors, c);
522 c->fn = reg->collect_fn;
523 c->entry_index = reg->entry_index;
524 c->vector_index = reg->vector_index;
525 c->private_data = reg->private_data;
526
527 return;
528}