stats: fix vector overrun in expend check
[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;
110
111   oldheap = clib_mem_set_heap (sm->heap);
112
113   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     }
123
124   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
132 void
133 vlib_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;
139   vlib_counter_t **vc;
140   u32 i;
141
142   if (entry_index >= vec_len (sm->directory_vector))
143     return;
144
145   oldheap = clib_mem_set_heap (sm->heap);
146
147   vlib_stats_segment_lock ();
148
149   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
165     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
173     case STAT_DIR_TYPE_SCALAR_INDEX:
174     case STAT_DIR_TYPE_SYMLINK:
175       break;
176     default:
177       ASSERT (0);
178     }
179
180   vlib_stats_segment_unlock ();
181
182   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;
187
188   e->value = sm->dir_vector_first_free_elt;
189   sm->dir_vector_first_free_elt = entry_index;
190 }
191
192 static void
193 vlib_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
207 static u32
208 vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
209 {
210   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
211   vlib_stats_shared_header_t *shared_header = sm->shared_header;
212   vlib_stats_entry_t e = { .type = t };
213
214   ASSERT (shared_header);
215
216   u32 vector_index = vlib_stats_find_entry_index ("%v", name);
217   if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
218     {
219       vector_index = ~0;
220       goto done;
221     }
222
223   vec_add1 (name, 0);
224   vlib_stats_set_entry_name (&e, (char *) name);
225
226   vlib_stats_segment_lock ();
227   vector_index = vlib_stats_create_counter (&e);
228
229   shared_header->directory_vector = sm->directory_vector;
230
231   vlib_stats_segment_unlock ();
232
233 done:
234   vec_free (name);
235   return vector_index;
236 }
237
238 u32
239 vlib_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);
248 }
249
250 void
251 vlib_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
259 u32
260 vlib_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
271 void
272 vlib_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
280 u32
281 vlib_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
292 void
293 vlib_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
316 u32
317 vlib_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
329 u32
330 vlib_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
342 static int
343 vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
344 {
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;
348   int rv = 1;
349
350   oldheap = clib_mem_set_heap (sm->heap);
351   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;
356
357       if (idx0 >= vec_len (data))
358         goto done;
359
360       for (u32 i = 0; i <= idx0; i++)
361         if (idx1 >= vec_max_len (data[i]))
362           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
372       if (idx0 >= vec_len (data))
373         goto done;
374
375       for (u32 i = 0; i <= idx0; i++)
376         if (idx1 >= vec_max_len (data[i]))
377           goto done;
378     }
379   else
380     ASSERT (0);
381
382   rv = 0;
383 done:
384   clib_mem_set_heap (oldheap);
385   return rv;
386 }
387
388 int
389 vlib_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
400 void
401 vlib_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 ();
453 }
454
455 u32
456 vlib_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
490 void
491 vlib_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
510 f64
511 vlib_stats_get_segment_update_rate (void)
512 {
513   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
514   return sm->update_interval;
515 }
516
517 void
518 vlib_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 }