stats: fix vlib_stats_validate_will_expand_internal
[vpp.git] / src / vlib / stats / stats.c
1 /* 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
8 vlib_stats_main_t vlib_stats_main;
9
10 void
11 vlib_stats_segment_lock (void)
12 {
13   vlib_main_t *vm = vlib_get_main ();
14   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
15
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
25   clib_spinlock_lock (sm->stat_segment_lockp);
26
27   sm->shared_header->in_progress = 1;
28   sm->locking_thread_index = vm->thread_index;
29 done:
30   sm->n_locks++;
31 }
32
33 void
34 vlib_stats_segment_unlock (void)
35 {
36   vlib_main_t *vm = vlib_get_main ();
37   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
38
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
48   sm->shared_header->epoch++;
49   __atomic_store_n (&sm->shared_header->in_progress, 0, __ATOMIC_RELEASE);
50   sm->locking_thread_index = ~0;
51   clib_spinlock_unlock (sm->stat_segment_lockp);
52 }
53
54 /*
55  * Change heap to the stats shared memory segment
56  */
57 void *
58 vlib_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
66 u32
67 vlib_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
83 static void
84 hash_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
92 static void
93 hash_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
104 u32
105 vlib_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
131 void
132 vlib_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;
138   vlib_counter_t **vc;
139   u32 i;
140
141   if (entry_index >= vec_len (sm->directory_vector))
142     return;
143
144   oldheap = clib_mem_set_heap (sm->heap);
145
146   vlib_stats_segment_lock ();
147
148   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
164     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
172     case STAT_DIR_TYPE_SCALAR_INDEX:
173     case STAT_DIR_TYPE_SYMLINK:
174       break;
175     default:
176       ASSERT (0);
177     }
178
179   vlib_stats_segment_unlock ();
180
181   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
188 static void
189 vlib_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
203 static u32
204 vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
205 {
206   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
207   vlib_stats_shared_header_t *shared_header = sm->shared_header;
208   vlib_stats_entry_t e = { .type = t };
209
210   ASSERT (shared_header);
211
212   u32 vector_index = vlib_stats_find_entry_index ("%v", name);
213   if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
214     {
215       vector_index = ~0;
216       goto done;
217     }
218
219   vec_add1 (name, 0);
220   vlib_stats_set_entry_name (&e, (char *) name);
221
222   vlib_stats_segment_lock ();
223   vector_index = vlib_stats_create_counter (&e);
224
225   shared_header->directory_vector = sm->directory_vector;
226
227   vlib_stats_segment_unlock ();
228
229 done:
230   vec_free (name);
231   return vector_index;
232 }
233
234 u32
235 vlib_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);
244 }
245
246 void
247 vlib_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
280 void
281 vlib_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
304 void
305 vlib_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
313 u32
314 vlib_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
325 void
326 vlib_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
334 u32
335 vlib_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
346 void
347 vlib_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
370 u32
371 vlib_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
383 u32
384 vlib_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
396 static int
397 vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
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   int rv = 1;
403
404   oldheap = clib_mem_set_heap (sm->heap);
405   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;
410
411       if (idx0 >= vec_max_len (data))
412         goto done;
413
414       for (u32 i = 0; i <= idx0; i++)
415         if (idx1 >= vec_max_len (data[i]))
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[i]))
431           goto done;
432     }
433   else
434     ASSERT (0);
435
436   rv = 0;
437 done:
438   clib_mem_set_heap (oldheap);
439   return rv;
440 }
441
442 int
443 vlib_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
454 void
455 vlib_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 ();
507 }
508
509 u32
510 vlib_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
544 void
545 vlib_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
564 f64
565 vlib_stats_get_segment_update_rate (void)
566 {
567   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
568   return sm->update_interval;
569 }
570
571 void
572 vlib_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 }