300cfe785e178a9d083a1f6bd9b2b72ca7fb3352
[vpp.git] / src / vpp / stats / stat_segment.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vppinfra/mem.h>
17 #include <vlib/vlib.h>
18 #include <vlib/unix/unix.h>
19 #include "stat_segment.h"
20 #include <vnet/vnet.h>
21 #include <vnet/devices/devices.h>       /* vnet_get_aggregate_rx_packets */
22 #undef HAVE_MEMFD_CREATE
23 #include <vppinfra/linux/syscall.h>
24 #include <vpp-api/client/stat_client.h>
25 stat_segment_main_t stat_segment_main;
26
27 /*
28  *  Used only by VPP writers
29  */
30 void
31 vlib_stat_segment_lock (void)
32 {
33   stat_segment_main_t *sm = &stat_segment_main;
34   clib_spinlock_lock (sm->stat_segment_lockp);
35   sm->shared_header->in_progress = 1;
36 }
37
38 void
39 vlib_stat_segment_unlock (void)
40 {
41   stat_segment_main_t *sm = &stat_segment_main;
42   sm->shared_header->epoch++;
43   sm->shared_header->in_progress = 0;
44   clib_spinlock_unlock (sm->stat_segment_lockp);
45 }
46
47 /*
48  * Change heap to the stats shared memory segment
49  */
50 void *
51 vlib_stats_push_heap (void *old)
52 {
53   stat_segment_main_t *sm = &stat_segment_main;
54
55   sm->last = old;
56   ASSERT (sm && sm->shared_header);
57   return clib_mem_set_heap (sm->heap);
58 }
59
60 /* Name to vector index hash */
61 static u32
62 lookup_or_create_hash_index (void *oldheap, char *name, u32 next_vector_index)
63 {
64   stat_segment_main_t *sm = &stat_segment_main;
65   u32 index;
66   hash_pair_t *hp;
67
68   hp = hash_get_pair (sm->directory_vector_by_name, name);
69   if (!hp)
70     {
71       hash_set (sm->directory_vector_by_name, name, next_vector_index);
72       index = next_vector_index;
73     }
74   else
75     {
76       index = hp->value[0];
77     }
78
79   return index;
80 }
81
82 void
83 vlib_stats_pop_heap (void *cm_arg, void *oldheap, u32 cindex,
84                      stat_directory_type_t type)
85 {
86   vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
87   stat_segment_main_t *sm = &stat_segment_main;
88   stat_segment_shared_header_t *shared_header = sm->shared_header;
89   char *stat_segment_name;
90   stat_segment_directory_entry_t e = { 0 };
91
92   /* Not all counters have names / hash-table entries */
93   if (!cm->name && !cm->stat_segment_name)
94     {
95       clib_mem_set_heap (oldheap);
96       return;
97     }
98
99   ASSERT (shared_header);
100
101   vlib_stat_segment_lock ();
102
103   /* Lookup hash-table is on the main heap */
104   stat_segment_name =
105     cm->stat_segment_name ? cm->stat_segment_name : cm->name;
106   u32 next_vector_index = vec_len (sm->directory_vector);
107   clib_mem_set_heap (oldheap);  /* Exit stats segment */
108   u32 vector_index = lookup_or_create_hash_index (oldheap, stat_segment_name,
109                                                   next_vector_index);
110   /* Back to stats segment */
111   clib_mem_set_heap (sm->heap); /* Re-enter stat segment */
112
113
114   /* Update the vector */
115   if (vector_index == next_vector_index)
116     {                           /* New */
117       strncpy (e.name, stat_segment_name, 128 - 1);
118       e.type = type;
119       vec_add1 (sm->directory_vector, e);
120     }
121
122   stat_segment_directory_entry_t *ep = &sm->directory_vector[vector_index];
123   ep->offset = stat_segment_offset (shared_header, cm->counters);       /* Vector of threads of vectors of counters */
124   u64 *offset_vector =
125     ep->offset_vector ? stat_segment_pointer (shared_header,
126                                               ep->offset_vector) : 0;
127
128   /* Update the 2nd dimension offset vector */
129   int i;
130   vec_validate (offset_vector, vec_len (cm->counters) - 1);
131
132   if (sm->last != offset_vector)
133     {
134       for (i = 0; i < vec_len (cm->counters); i++)
135         offset_vector[i] =
136           stat_segment_offset (shared_header, cm->counters[i]);
137     }
138   else
139     offset_vector[cindex] =
140       stat_segment_offset (shared_header, cm->counters[cindex]);
141
142   ep->offset_vector = stat_segment_offset (shared_header, offset_vector);
143   sm->directory_vector[vector_index].offset =
144     stat_segment_offset (shared_header, cm->counters);
145
146   /* Reset the client hash table pointer, since it WILL change! */
147   shared_header->directory_offset =
148     stat_segment_offset (shared_header, sm->directory_vector);
149
150   vlib_stat_segment_unlock ();
151   clib_mem_set_heap (oldheap);
152 }
153
154 void
155 vlib_stats_register_error_index (u8 * name, u64 * em_vec, u64 index)
156 {
157   stat_segment_main_t *sm = &stat_segment_main;
158   stat_segment_shared_header_t *shared_header = sm->shared_header;
159   stat_segment_directory_entry_t e;
160   hash_pair_t *hp;
161
162   ASSERT (shared_header);
163
164   vlib_stat_segment_lock ();
165
166   memcpy (e.name, name, vec_len (name));
167   e.name[vec_len (name)] = '\0';
168   e.type = STAT_DIR_TYPE_ERROR_INDEX;
169   e.offset = index;
170   e.offset_vector = 0;
171   vec_add1 (sm->directory_vector, e);
172
173   /* Warn clients to refresh any pointers they might be holding */
174   shared_header->directory_offset =
175     stat_segment_offset (shared_header, sm->directory_vector);
176
177   vlib_stat_segment_unlock ();
178 }
179
180 static void
181 stat_validate_counter_vector (stat_segment_directory_entry_t * ep, u32 max)
182 {
183   stat_segment_main_t *sm = &stat_segment_main;
184   stat_segment_shared_header_t *shared_header = sm->shared_header;
185   counter_t **counters = 0;
186   vlib_thread_main_t *tm = vlib_get_thread_main ();
187   int i;
188   u64 *offset_vector = 0;
189
190   vec_validate_aligned (counters, tm->n_vlib_mains - 1,
191                         CLIB_CACHE_LINE_BYTES);
192   for (i = 0; i < tm->n_vlib_mains; i++)
193     {
194       vec_validate_aligned (counters[i], max, CLIB_CACHE_LINE_BYTES);
195       vec_add1 (offset_vector,
196                 stat_segment_offset (shared_header, counters[i]));
197     }
198   ep->offset = stat_segment_offset (shared_header, counters);
199   ep->offset_vector = stat_segment_offset (shared_header, offset_vector);
200 }
201
202 void
203 vlib_stats_pop_heap2 (u64 * error_vector, u32 thread_index, void *oldheap)
204 {
205   stat_segment_main_t *sm = &stat_segment_main;
206   stat_segment_shared_header_t *shared_header = sm->shared_header;
207
208   ASSERT (shared_header);
209
210   vlib_stat_segment_lock ();
211
212   /* Reset the client hash table pointer, since it WILL change! */
213   shared_header->error_offset =
214     stat_segment_offset (shared_header, error_vector);
215   shared_header->directory_offset =
216     stat_segment_offset (shared_header, sm->directory_vector);
217
218   vlib_stat_segment_unlock ();
219   clib_mem_set_heap (oldheap);
220 }
221
222 clib_error_t *
223 vlib_map_stat_segment_init (void)
224 {
225   stat_segment_main_t *sm = &stat_segment_main;
226   stat_segment_shared_header_t *shared_header;
227   stat_segment_directory_entry_t *ep;
228   void *oldheap;
229   ssize_t memory_size;
230   int mfd;
231   char *mem_name = "stat_segment_test";
232   void *memaddr;
233
234   memory_size = sm->memory_size;
235   if (memory_size == 0)
236     memory_size = STAT_SEGMENT_DEFAULT_SIZE;
237
238   /* Create shared memory segment */
239   if ((mfd = memfd_create (mem_name, 0)) < 0)
240     return clib_error_return (0, "stat segment memfd_create failure");
241
242   /* Set size */
243   if ((ftruncate (mfd, memory_size)) == -1)
244     return clib_error_return (0, "stat segment ftruncate failure");
245
246   if ((memaddr =
247        mmap (NULL, memory_size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd,
248              0)) == MAP_FAILED)
249     return clib_error_return (0, "stat segment mmap failure");
250
251   void *heap;
252 #if USE_DLMALLOC == 0
253   heap = mheap_alloc_with_flags (((u8 *) memaddr) + getpagesize (),
254                                  memory_size - getpagesize (),
255                                  MHEAP_FLAG_DISABLE_VM |
256                                  MHEAP_FLAG_THREAD_SAFE);
257 #else
258   heap =
259     create_mspace_with_base (((u8 *) memaddr) + getpagesize (),
260                              memory_size - getpagesize (), 1 /* locked */ );
261   mspace_disable_expand (heap);
262 #endif
263
264   sm->heap = heap;
265   sm->memfd = mfd;
266
267   sm->directory_vector_by_name = hash_create_string (0, sizeof (uword));
268   sm->shared_header = shared_header = memaddr;
269   sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
270   clib_spinlock_init (sm->stat_segment_lockp);
271
272   oldheap = clib_mem_set_heap (sm->heap);
273
274   /* Set up the name to counter-vector hash table */
275   sm->directory_vector = 0;
276
277   shared_header->epoch = 1;
278
279   /* Scalar stats and node counters */
280   vec_validate (sm->directory_vector, STAT_COUNTERS);
281 #define _(E,t,n,p)                                                      \
282   strcpy(sm->directory_vector[STAT_COUNTER_##E].name,  #p "/" #n); \
283   sm->directory_vector[STAT_COUNTER_##E].type = STAT_DIR_TYPE_##t;
284   foreach_stat_segment_counter_name
285 #undef _
286     /* Save the vector offset in the shared segment, for clients */
287     shared_header->directory_offset =
288     stat_segment_offset (shared_header, sm->directory_vector);
289
290   clib_mem_set_heap (oldheap);
291
292   return 0;
293 }
294
295 static int
296 name_sort_cmp (void *a1, void *a2)
297 {
298   stat_segment_directory_entry_t *n1 = a1;
299   stat_segment_directory_entry_t *n2 = a2;
300
301   return strcmp ((char *) n1->name, (char *) n2->name);
302 }
303
304 static u8 *
305 format_stat_dir_entry (u8 * s, va_list * args)
306 {
307   stat_segment_directory_entry_t *ep =
308     va_arg (*args, stat_segment_directory_entry_t *);
309   char *type_name;
310   char *format_string;
311
312   format_string = "%-74s %-10s %10lld";
313
314   switch (ep->type)
315     {
316     case STAT_DIR_TYPE_SCALAR_INDEX:
317       type_name = "ScalarPtr";
318       break;
319
320     case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
321     case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
322       type_name = "CMainPtr";
323       break;
324
325     case STAT_DIR_TYPE_ERROR_INDEX:
326       type_name = "ErrIndex";
327       break;
328
329     default:
330       type_name = "illegal!";
331       break;
332     }
333
334   return format (s, format_string, ep->name, type_name, ep->offset);
335 }
336
337 static clib_error_t *
338 show_stat_segment_command_fn (vlib_main_t * vm,
339                               unformat_input_t * input,
340                               vlib_cli_command_t * cmd)
341 {
342   stat_segment_main_t *sm = &stat_segment_main;
343   counter_t *counter;
344   hash_pair_t *p;
345   stat_segment_directory_entry_t *show_data, *this;
346   int i, j;
347
348   int verbose = 0;
349   u8 *s;
350
351   if (unformat (input, "verbose"))
352     verbose = 1;
353
354   /* Lock even as reader, as this command doesn't handle epoch changes */
355   vlib_stat_segment_lock ();
356   show_data = vec_dup (sm->directory_vector);
357   vlib_stat_segment_unlock ();
358
359   vec_sort_with_function (show_data, name_sort_cmp);
360
361   vlib_cli_output (vm, "%-74s %10s %10s", "Name", "Type", "Value");
362
363   for (i = 0; i < vec_len (show_data); i++)
364     {
365       vlib_cli_output (vm, "%-100U", format_stat_dir_entry,
366                        vec_elt_at_index (show_data, i));
367     }
368
369   if (verbose)
370     {
371       ASSERT (sm->heap);
372       vlib_cli_output (vm, "%U", format_mheap, sm->heap, 0 /* verbose */ );
373     }
374
375   return 0;
376 }
377
378 /* *INDENT-OFF* */
379 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
380 {
381   .path = "show statistics segment",
382   .short_help = "show statistics segment [verbose]",
383   .function = show_stat_segment_command_fn,
384 };
385 /* *INDENT-ON* */
386
387 /*
388  * Node performance counters:
389  * total_calls [threads][node-index]
390  * total_vectors
391  * total_calls
392  * total suspends
393  */
394
395 static inline void
396 update_node_counters (stat_segment_main_t * sm)
397 {
398   vlib_main_t *vm = vlib_mains[0];
399   vlib_main_t **stat_vms = 0;
400   vlib_node_t ***node_dups = 0;
401   int i, j;
402   stat_segment_shared_header_t *shared_header = sm->shared_header;
403   static u32 no_max_nodes = 0;
404
405   vlib_node_get_nodes (0 /* vm, for barrier sync */ ,
406                        (u32) ~ 0 /* all threads */ ,
407                        1 /* include stats */ ,
408                        0 /* barrier sync */ ,
409                        &node_dups, &stat_vms);
410
411   u32 l = vec_len (node_dups[0]);
412
413   /*
414    * Extend performance nodes if necessary
415    */
416   if (l > no_max_nodes)
417     {
418       void *oldheap = clib_mem_set_heap (sm->heap);
419       vlib_stat_segment_lock ();
420
421       stat_validate_counter_vector (&sm->directory_vector
422                                     [STAT_COUNTER_NODE_CLOCKS], l);
423       stat_validate_counter_vector (&sm->directory_vector
424                                     [STAT_COUNTER_NODE_VECTORS], l);
425       stat_validate_counter_vector (&sm->directory_vector
426                                     [STAT_COUNTER_NODE_CALLS], l);
427       stat_validate_counter_vector (&sm->directory_vector
428                                     [STAT_COUNTER_NODE_SUSPENDS], l);
429
430       vec_validate (sm->nodes, l - 1);
431       stat_segment_directory_entry_t *ep;
432       ep = &sm->directory_vector[STAT_COUNTER_NODE_NAMES];
433       ep->offset = stat_segment_offset (shared_header, sm->nodes);
434
435       int i;
436       u64 *offset_vector =
437         ep->offset_vector ? stat_segment_pointer (shared_header,
438                                                   ep->offset_vector) : 0;
439       /* Update names dictionary */
440       vec_validate (offset_vector, l - 1);
441       vlib_node_t **nodes = node_dups[0];
442
443       for (i = 0; i < vec_len (nodes); i++)
444         {
445           vlib_node_t *n = nodes[i];
446           u8 *s = 0;
447           s = format (s, "%v%c", n->name, 0);
448           if (sm->nodes[n->index])
449             vec_free (sm->nodes[n->index]);
450           sm->nodes[n->index] = s;
451           offset_vector[i] =
452             sm->nodes[i] ? stat_segment_offset (shared_header,
453                                                 sm->nodes[i]) : 0;
454
455         }
456       ep->offset_vector = stat_segment_offset (shared_header, offset_vector);
457
458       vlib_stat_segment_unlock ();
459       clib_mem_set_heap (oldheap);
460       no_max_nodes = l;
461     }
462
463   for (j = 0; j < vec_len (node_dups); j++)
464     {
465       vlib_node_t **nodes = node_dups[j];
466       u32 l = vec_len (nodes);
467
468       for (i = 0; i < vec_len (nodes); i++)
469         {
470           counter_t **counters;
471           counter_t *c;
472           vlib_node_t *n = nodes[i];
473
474           counters =
475             stat_segment_pointer (shared_header,
476                                   sm->directory_vector
477                                   [STAT_COUNTER_NODE_CLOCKS].offset);
478           c = counters[j];
479           c[n->index] = n->stats_total.clocks - n->stats_last_clear.clocks;
480
481           counters =
482             stat_segment_pointer (shared_header,
483                                   sm->directory_vector
484                                   [STAT_COUNTER_NODE_VECTORS].offset);
485           c = counters[j];
486           c[n->index] = n->stats_total.vectors - n->stats_last_clear.vectors;
487
488           counters =
489             stat_segment_pointer (shared_header,
490                                   sm->directory_vector
491                                   [STAT_COUNTER_NODE_CALLS].offset);
492           c = counters[j];
493           c[n->index] = n->stats_total.calls - n->stats_last_clear.calls;
494
495           counters =
496             stat_segment_pointer (shared_header,
497                                   sm->directory_vector
498                                   [STAT_COUNTER_NODE_SUSPENDS].offset);
499           c = counters[j];
500           c[n->index] =
501             n->stats_total.suspends - n->stats_last_clear.suspends;
502         }
503     }
504 }
505
506 static void
507 do_stat_segment_updates (stat_segment_main_t * sm)
508 {
509   stat_segment_shared_header_t *shared_header = sm->shared_header;
510   vlib_main_t *vm = vlib_mains[0];
511   f64 vector_rate;
512   u64 input_packets, last_input_packets;
513   f64 dt, now;
514   vlib_main_t *this_vlib_main;
515   int i, start;
516   counter_t **counters;
517   static int num_worker_threads_set;
518
519   /*
520    * Set once at the beginning of time.
521    * Can't do this from the init routine, which happens before
522    * start_workers sets up vlib_mains...
523    */
524   if (PREDICT_FALSE (num_worker_threads_set == 0))
525     {
526       sm->directory_vector[STAT_COUNTER_NUM_WORKER_THREADS].value =
527         vec_len (vlib_mains) > 1 ? vec_len (vlib_mains) - 1 : 1;
528
529       stat_validate_counter_vector (&sm->directory_vector
530                                     [STAT_COUNTER_VECTOR_RATE_PER_WORKER],
531                                     vec_len (vlib_mains));
532       num_worker_threads_set = 1;
533     }
534
535   /*
536    * Compute per-worker vector rates, and the average vector rate
537    * across all workers
538    */
539   vector_rate = 0.0;
540
541   counters =
542     stat_segment_pointer (shared_header,
543                           sm->directory_vector
544                           [STAT_COUNTER_VECTOR_RATE_PER_WORKER].offset);
545
546   start = vec_len (vlib_mains) > 1 ? 1 : 0;
547
548   for (i = start; i < vec_len (vlib_mains); i++)
549     {
550
551       f64 this_vector_rate;
552
553       this_vlib_main = vlib_mains[i];
554
555       this_vector_rate = vlib_last_vector_length_per_node (this_vlib_main);
556       vector_rate += this_vector_rate;
557
558       /* Set the per-worker rate */
559       counters[i - start][0] = this_vector_rate;
560     }
561
562   /* And set the system average rate */
563   vector_rate /= (f64) (i - start);
564
565   sm->directory_vector[STAT_COUNTER_VECTOR_RATE].value = vector_rate;
566
567   /*
568    * Compute the aggregate input rate
569    */
570   now = vlib_time_now (vm);
571   dt = now - sm->directory_vector[STAT_COUNTER_LAST_UPDATE].value;
572   input_packets = vnet_get_aggregate_rx_packets ();
573   sm->directory_vector[STAT_COUNTER_INPUT_RATE].value =
574     (f64) (input_packets - sm->last_input_packets) / dt;
575   sm->directory_vector[STAT_COUNTER_LAST_UPDATE].value = now;
576   sm->last_input_packets = input_packets;
577   sm->directory_vector[STAT_COUNTER_LAST_STATS_CLEAR].value =
578     vm->node_main.time_last_runtime_stats_clear;
579
580   if (sm->node_counters_enabled)
581     update_node_counters (sm);
582
583   /* *INDENT-OFF* */
584   stat_segment_gauges_pool_t *g;
585   pool_foreach(g, sm->gauges,
586   ({
587     g->fn(&sm->directory_vector[g->directory_index], g->caller_index);
588   }));
589   /* *INDENT-ON* */
590
591   /* Heartbeat, so clients detect we're still here */
592   sm->directory_vector[STAT_COUNTER_HEARTBEAT].value++;
593 }
594
595 /*
596  * Accept connection on the socket and exchange the fd for the shared
597  * memory segment.
598  */
599 static clib_error_t *
600 stats_socket_accept_ready (clib_file_t * uf)
601 {
602   stat_segment_main_t *sm = &stat_segment_main;
603   clib_error_t *err;
604   clib_socket_t client = { 0 };
605
606   err = clib_socket_accept (sm->socket, &client);
607   if (err)
608     {
609       clib_error_report (err);
610       return err;
611     }
612
613   /* Send the fd across and close */
614   err = clib_socket_sendmsg (&client, 0, 0, &sm->memfd, 1);
615   if (err)
616     clib_error_report (err);
617   clib_socket_close (&client);
618
619   return 0;
620 }
621
622 static void
623 stats_segment_socket_init (void)
624 {
625   stat_segment_main_t *sm = &stat_segment_main;
626   clib_error_t *error;
627   clib_socket_t *s = clib_mem_alloc (sizeof (clib_socket_t));
628
629   memset (s, 0, sizeof (clib_socket_t));
630   s->config = (char *) sm->socket_name;
631   s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_SEQPACKET |
632     CLIB_SOCKET_F_ALLOW_GROUP_WRITE | CLIB_SOCKET_F_PASSCRED;
633
634   if ((error = clib_socket_init (s)))
635     {
636       clib_error_report (error);
637       return;
638     }
639
640   clib_file_t template = { 0 };
641   template.read_function = stats_socket_accept_ready;
642   template.file_descriptor = s->fd;
643   template.description = format (0, "stats segment listener %s", s->config);
644   clib_file_add (&file_main, &template);
645
646   sm->socket = s;
647 }
648
649 static clib_error_t *
650 stats_segment_socket_exit (vlib_main_t * vm)
651 {
652   /*
653    * cleanup the listener socket on exit.
654    */
655   stat_segment_main_t *sm = &stat_segment_main;
656   unlink ((char *) sm->socket_name);
657   return 0;
658 }
659
660 VLIB_MAIN_LOOP_EXIT_FUNCTION (stats_segment_socket_exit);
661
662 static uword
663 stat_segment_collector_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
664                                 vlib_frame_t * f)
665 {
666   stat_segment_main_t *sm = &stat_segment_main;
667
668   /* Wait for Godot... */
669   f64 sleep_duration = 10;
670
671   while (1)
672     {
673       do_stat_segment_updates (sm);
674       vlib_process_suspend (vm, sleep_duration);
675     }
676   return 0;                     /* or not */
677 }
678
679 static clib_error_t *
680 statseg_init (vlib_main_t * vm)
681 {
682   stat_segment_main_t *sm = &stat_segment_main;
683   clib_error_t *error;
684
685   /* dependent on unix_input_init */
686   if ((error = vlib_call_init_function (vm, unix_input_init)))
687     return error;
688
689   if (sm->socket_name)
690     stats_segment_socket_init ();
691
692   return 0;
693 }
694
695 clib_error_t *
696 stat_segment_register_gauge (u8 * name, stat_segment_update_fn update_fn,
697                              u32 caller_index)
698 {
699   stat_segment_main_t *sm = &stat_segment_main;
700   stat_segment_shared_header_t *shared_header = sm->shared_header;
701   void *oldheap;
702   stat_segment_directory_entry_t e;
703   u32 index;
704   stat_segment_gauges_pool_t *gauge;
705
706   ASSERT (shared_header);
707
708   oldheap = vlib_stats_push_heap (NULL);
709   vlib_stat_segment_lock ();
710
711   memset (&e, 0, sizeof (e));
712   e.type = STAT_DIR_TYPE_SCALAR_INDEX;
713
714   memcpy (e.name, name, vec_len (name));
715   index = vec_len (sm->directory_vector);
716   vec_add1 (sm->directory_vector, e);
717
718   shared_header->directory_offset =
719     stat_segment_offset (shared_header, sm->directory_vector);
720
721   vlib_stat_segment_unlock ();
722   clib_mem_set_heap (oldheap);
723
724   /* Back on our own heap */
725   pool_get (sm->gauges, gauge);
726   gauge->fn = update_fn;
727   gauge->caller_index = caller_index;
728   gauge->directory_index = index;
729
730   return NULL;
731 }
732
733 static clib_error_t *
734 statseg_config (vlib_main_t * vm, unformat_input_t * input)
735 {
736   stat_segment_main_t *sm = &stat_segment_main;
737
738   /* set default socket file name when statseg config stanza is empty. */
739   sm->socket_name = format (0, "%s", STAT_SEGMENT_SOCKET_FILE);
740   /*
741    * NULL-terminate socket name string
742    * clib_socket_init()->socket_config() use C str*
743    */
744   vec_add1 (sm->socket_name, 0);
745
746   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
747     {
748       if (unformat (input, "socket-name %s", &sm->socket_name))
749         ;
750       else if (unformat (input, "default"))
751         sm->socket_name = format (0, "%s", STAT_SEGMENT_SOCKET_FILE);
752       else
753         if (unformat
754             (input, "size %U", unformat_memory_size, &sm->memory_size))
755         ;
756       else if (unformat (input, "per-node-counters on"))
757         sm->node_counters_enabled = 1;
758       else if (unformat (input, "per-node-counters off"))
759         sm->node_counters_enabled = 0;
760       else
761         return clib_error_return (0, "unknown input `%U'",
762                                   format_unformat_error, input);
763     }
764   return 0;
765 }
766
767 static clib_error_t *
768 statseg_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
769 {
770   stat_segment_main_t *sm = &stat_segment_main;
771   stat_segment_shared_header_t *shared_header = sm->shared_header;
772
773   void *oldheap = vlib_stats_push_heap (sm->interfaces);
774   vlib_stat_segment_lock ();
775
776   vec_validate (sm->interfaces, sw_if_index);
777   if (is_add)
778     {
779       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
780       vnet_sw_interface_t *si_sup =
781         vnet_get_sup_sw_interface (vnm, si->sw_if_index);
782       vnet_hw_interface_t *hi_sup;
783
784       ASSERT (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
785       hi_sup = vnet_get_hw_interface (vnm, si_sup->hw_if_index);
786
787       u8 *s = 0;
788       s = format (s, "%v", hi_sup->name);
789       if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
790         s = format (s, ".%d", si->sub.id);
791       s = format (s, "%c", 0);
792       sm->interfaces[sw_if_index] = s;
793     }
794   else
795     {
796       vec_free (sm->interfaces[sw_if_index]);
797       sm->interfaces[sw_if_index] = 0;
798     }
799
800   stat_segment_directory_entry_t *ep;
801   ep = &sm->directory_vector[STAT_COUNTER_INTERFACE_NAMES];
802   ep->offset = stat_segment_offset (shared_header, sm->interfaces);
803
804   int i;
805   u64 *offset_vector =
806     ep->offset_vector ? stat_segment_pointer (shared_header,
807                                               ep->offset_vector) : 0;
808
809   vec_validate (offset_vector, vec_len (sm->interfaces) - 1);
810
811   if (sm->last != sm->interfaces)
812     {
813       /* the interface vector moved, so need to recalulate the offset array */
814       for (i = 0; i < vec_len (sm->interfaces); i++)
815         {
816           offset_vector[i] =
817             sm->interfaces[i] ? stat_segment_offset (shared_header,
818                                                      sm->interfaces[i]) : 0;
819         }
820     }
821   else
822     {
823       offset_vector[sw_if_index] =
824         sm->interfaces[sw_if_index] ?
825         stat_segment_offset (shared_header, sm->interfaces[sw_if_index]) : 0;
826     }
827   ep->offset_vector = stat_segment_offset (shared_header, offset_vector);
828
829   vlib_stat_segment_unlock ();
830   clib_mem_set_heap (oldheap);
831
832   return 0;
833 }
834
835 VLIB_INIT_FUNCTION (statseg_init);
836 VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg");
837 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (statseg_sw_interface_add_del);
838
839 /* *INDENT-OFF* */
840 VLIB_REGISTER_NODE (stat_segment_collector, static) =
841 {
842 .function = stat_segment_collector_process,
843 .name = "statseg-collector-process",
844 .type = VLIB_NODE_TYPE_PROCESS,
845 };
846
847 /* *INDENT-ON* */
848
849 /*
850  * fd.io coding-style-patch-verification: ON
851  *
852  * Local Variables:
853  * eval: (c-set-style "gnu")
854  * End:
855  */