STATS: Refactor missed adding messages to CRC dictionary.
[vpp.git] / src / vpp / stats / stats.c
1 /*
2  * Copyright (c) 2015 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 #include <vpp/stats/stats.h>
16 #include <signal.h>
17 #include <vnet/fib/ip4_fib.h>
18 #include <vnet/fib/fib_entry.h>
19 #include <vnet/dpo/load_balance.h>
20
21 #define STATS_DEBUG 0
22
23 stats_main_t stats_main;
24
25 #include <vnet/ip/ip.h>
26
27 #include <vpp/api/vpe_msg_enum.h>
28
29 #define f64_endian(a)
30 #define f64_print(a,b)
31
32 #define vl_typedefs             /* define message structures */
33 #include <vpp/api/vpe_all_api_h.h>
34 #undef vl_typedefs
35
36 #define vl_endianfun            /* define message structures */
37 #include <vpp/api/vpe_all_api_h.h>
38 #undef vl_endianfun
39
40 /* instantiate all the print functions we know about */
41 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
42 #define vl_printfun
43 #include <vpp/api/vpe_all_api_h.h>
44 #undef vl_printfun
45
46 #define foreach_stats_msg                                               \
47 _(WANT_STATS, want_stats)                                               \
48 _(VNET_INTERFACE_SIMPLE_COUNTERS, vnet_interface_simple_counters)       \
49 _(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats)     \
50 _(VNET_INTERFACE_COMBINED_COUNTERS, vnet_interface_combined_counters)   \
51 _(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats) \
52 _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)                         \
53 _(WANT_IP4_FIB_STATS, want_ip4_fib_stats)            \
54 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)                         \
55 _(WANT_IP6_FIB_STATS, want_ip6_fib_stats)        \
56 _(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters)                         \
57 _(WANT_IP4_NBR_STATS, want_ip4_nbr_stats)            \
58 _(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters) \
59 _(WANT_IP6_NBR_STATS, want_ip6_nbr_stats) \
60 _(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats)
61
62
63 #define vl_msg_name_crc_list
64 #include <vpp/stats/stats.api.h>
65 #undef vl_msg_name_crc_list
66
67 static void
68 setup_message_id_table (api_main_t * am)
69 {
70 #define _(id,n,crc) \
71   vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
72   foreach_vl_msg_name_crc_stats;
73 #undef _
74 }
75
76 /* These constants ensure msg sizes <= 1024, aka ring allocation */
77 #define SIMPLE_COUNTER_BATCH_SIZE       126
78 #define COMBINED_COUNTER_BATCH_SIZE     63
79 #define IP4_FIB_COUNTER_BATCH_SIZE      48
80 #define IP6_FIB_COUNTER_BATCH_SIZE      30
81
82 /* 5ms */
83 #define STATS_RELEASE_DELAY_NS (1000 * 1000 * 5)
84 /*                              ns/us  us/ms        */
85
86 u8 *
87 format_vnet_interface_combined_counters (u8 * s, va_list * args)
88 {
89   stats_main_t *sm = &stats_main;
90   vl_api_vnet_interface_combined_counters_t *mp =
91     va_arg (*args, vl_api_vnet_interface_combined_counters_t *);
92
93   char *counter_name;
94   u32 count, sw_if_index;
95   int i;
96   count = ntohl (mp->count);
97   sw_if_index = ntohl (mp->first_sw_if_index);
98
99   vlib_counter_t *vp;
100   u64 packets, bytes;
101   vp = (vlib_counter_t *) mp->data;
102
103   switch (mp->vnet_counter_type)
104     {
105     case VNET_INTERFACE_COUNTER_RX:
106       counter_name = "rx";
107       break;
108     case VNET_INTERFACE_COUNTER_TX:
109       counter_name = "tx";
110       break;
111     default:
112       counter_name = "bogus";
113       break;
114     }
115   for (i = 0; i < count; i++)
116     {
117       packets = clib_mem_unaligned (&vp->packets, u64);
118       packets = clib_net_to_host_u64 (packets);
119       bytes = clib_mem_unaligned (&vp->bytes, u64);
120       bytes = clib_net_to_host_u64 (bytes);
121       vp++;
122       s = format (s, "%U.%s.packets %lld\n",
123                   format_vnet_sw_if_index_name,
124                   sm->vnet_main, sw_if_index, counter_name, packets);
125       s = format (s, "%U.%s.bytes %lld\n",
126                   format_vnet_sw_if_index_name,
127                   sm->vnet_main, sw_if_index, counter_name, bytes);
128       sw_if_index++;
129     }
130   return s;
131 }
132
133 u8 *
134 format_vnet_interface_simple_counters (u8 * s, va_list * args)
135 {
136   stats_main_t *sm = &stats_main;
137   vl_api_vnet_interface_simple_counters_t *mp =
138     va_arg (*args, vl_api_vnet_interface_simple_counters_t *);
139   char *counter_name;
140   u32 count, sw_if_index;
141   count = ntohl (mp->count);
142   sw_if_index = ntohl (mp->first_sw_if_index);
143   u64 *vp, v;
144   vp = (u64 *) mp->data;
145   int i;
146
147   switch (mp->vnet_counter_type)
148     {
149     case VNET_INTERFACE_COUNTER_DROP:
150       counter_name = "drop";
151       break;
152     case VNET_INTERFACE_COUNTER_PUNT:
153       counter_name = "punt";
154       break;
155     case VNET_INTERFACE_COUNTER_IP4:
156       counter_name = "ip4";
157       break;
158     case VNET_INTERFACE_COUNTER_IP6:
159       counter_name = "ip6";
160       break;
161     case VNET_INTERFACE_COUNTER_RX_NO_BUF:
162       counter_name = "rx-no-buff";
163       break;
164     case VNET_INTERFACE_COUNTER_RX_MISS:
165       counter_name = "rx-miss";
166       break;
167     case VNET_INTERFACE_COUNTER_RX_ERROR:
168       counter_name = "rx-error (fifo-full)";
169       break;
170     case VNET_INTERFACE_COUNTER_TX_ERROR:
171       counter_name = "tx-error (fifo-full)";
172       break;
173     default:
174       counter_name = "bogus";
175       break;
176     }
177   for (i = 0; i < count; i++)
178     {
179       v = clib_mem_unaligned (vp, u64);
180       v = clib_net_to_host_u64 (v);
181       vp++;
182       s = format (s, "%U.%s %lld\n", format_vnet_sw_if_index_name,
183                   sm->vnet_main, sw_if_index, counter_name, v);
184       sw_if_index++;
185     }
186
187   return s;
188 }
189
190 void
191 dslock (stats_main_t * sm, int release_hint, int tag)
192 {
193   u32 thread_index;
194   data_structure_lock_t *l = sm->data_structure_lock;
195
196   if (PREDICT_FALSE (l == 0))
197     return;
198
199   thread_index = vlib_get_thread_index ();
200   if (l->lock && l->thread_index == thread_index)
201     {
202       l->count++;
203       return;
204     }
205
206   if (release_hint)
207     l->release_hint++;
208
209   while (__sync_lock_test_and_set (&l->lock, 1))
210     /* zzzz */ ;
211   l->tag = tag;
212   l->thread_index = thread_index;
213   l->count = 1;
214 }
215
216 void
217 stats_dslock_with_hint (int hint, int tag)
218 {
219   stats_main_t *sm = &stats_main;
220   dslock (sm, hint, tag);
221 }
222
223 void
224 dsunlock (stats_main_t * sm)
225 {
226   u32 thread_index;
227   data_structure_lock_t *l = sm->data_structure_lock;
228
229   if (PREDICT_FALSE (l == 0))
230     return;
231
232   thread_index = vlib_get_thread_index ();
233   ASSERT (l->lock && l->thread_index == thread_index);
234   l->count--;
235   if (l->count == 0)
236     {
237       l->tag = -l->tag;
238       l->release_hint = 0;
239       CLIB_MEMORY_BARRIER ();
240       l->lock = 0;
241     }
242 }
243
244 void
245 stats_dsunlock (int hint, int tag)
246 {
247   stats_main_t *sm = &stats_main;
248   dsunlock (sm);
249 }
250
251 static void
252 do_simple_interface_counters (stats_main_t * sm)
253 {
254   vl_api_vnet_interface_simple_counters_t *mp = 0;
255   vnet_interface_main_t *im = sm->interface_main;
256   api_main_t *am = sm->api_main;
257   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
258   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
259   vlib_simple_counter_main_t *cm;
260   u32 items_this_message = 0;
261   u64 v, *vp = 0;
262   int i, n_counts;
263
264   /*
265    * Prevent interface registration from expanding / moving the vectors...
266    * That tends never to happen, so we can hold this lock for a while.
267    */
268   vnet_interface_counter_lock (im);
269
270   vec_foreach (cm, im->sw_if_counters)
271   {
272     n_counts = vlib_simple_counter_n_counters (cm);
273     for (i = 0; i < n_counts; i++)
274       {
275         if (mp == 0)
276           {
277             items_this_message = clib_min (SIMPLE_COUNTER_BATCH_SIZE,
278                                            n_counts - i);
279
280             mp = vl_msg_api_alloc_as_if_client
281               (sizeof (*mp) + items_this_message * sizeof (v));
282             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_SIMPLE_COUNTERS);
283             mp->vnet_counter_type = cm - im->sw_if_counters;
284             mp->first_sw_if_index = htonl (i);
285             mp->count = 0;
286             vp = (u64 *) mp->data;
287           }
288         v = vlib_get_simple_counter (cm, i);
289         clib_mem_unaligned (vp, u64) = clib_host_to_net_u64 (v);
290         vp++;
291         mp->count++;
292         if (mp->count == items_this_message)
293           {
294             mp->count = htonl (items_this_message);
295             /* Send to the main thread... */
296             vl_msg_api_send_shmem (q, (u8 *) & mp);
297             mp = 0;
298           }
299       }
300     ASSERT (mp == 0);
301   }
302   vnet_interface_counter_unlock (im);
303 }
304
305 static void
306 do_combined_interface_counters (stats_main_t * sm)
307 {
308   vl_api_vnet_interface_combined_counters_t *mp = 0;
309   vnet_interface_main_t *im = sm->interface_main;
310   api_main_t *am = sm->api_main;
311   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
312   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
313   vlib_combined_counter_main_t *cm;
314   u32 items_this_message = 0;
315   vlib_counter_t v, *vp = 0;
316   int i, n_counts;
317
318   vnet_interface_counter_lock (im);
319
320   vec_foreach (cm, im->combined_sw_if_counters)
321   {
322     n_counts = vlib_combined_counter_n_counters (cm);
323     for (i = 0; i < n_counts; i++)
324       {
325         if (mp == 0)
326           {
327             items_this_message = clib_min (COMBINED_COUNTER_BATCH_SIZE,
328                                            n_counts - i);
329
330             mp = vl_msg_api_alloc_as_if_client
331               (sizeof (*mp) + items_this_message * sizeof (v));
332             mp->_vl_msg_id = ntohs (VL_API_VNET_INTERFACE_COMBINED_COUNTERS);
333             mp->vnet_counter_type = cm - im->combined_sw_if_counters;
334             mp->first_sw_if_index = htonl (i);
335             mp->count = 0;
336             vp = (vlib_counter_t *) mp->data;
337           }
338         vlib_get_combined_counter (cm, i, &v);
339         clib_mem_unaligned (&vp->packets, u64)
340           = clib_host_to_net_u64 (v.packets);
341         clib_mem_unaligned (&vp->bytes, u64) = clib_host_to_net_u64 (v.bytes);
342         vp++;
343         mp->count++;
344         if (mp->count == items_this_message)
345           {
346             mp->count = htonl (items_this_message);
347             /* Send to the main thread... */
348             vl_msg_api_send_shmem (q, (u8 *) & mp);
349             mp = 0;
350           }
351       }
352     ASSERT (mp == 0);
353   }
354   vnet_interface_counter_unlock (im);
355 }
356
357 static void
358 ip46_fib_stats_delay (stats_main_t * sm, u32 sec, u32 nsec)
359 {
360   struct timespec _req, *req = &_req;
361   struct timespec _rem, *rem = &_rem;
362
363   req->tv_sec = sec;
364   req->tv_nsec = nsec;
365   while (1)
366     {
367       if (nanosleep (req, rem) == 0)
368         break;
369       *req = *rem;
370       if (errno == EINTR)
371         continue;
372       clib_unix_warning ("nanosleep");
373       break;
374     }
375 }
376
377 /**
378  * @brief The context passed when collecting adjacency counters
379  */
380 typedef struct ip4_nbr_stats_ctx_t_
381 {
382   /**
383    * The SW IF index all these adjs belong to
384    */
385   u32 sw_if_index;
386
387   /**
388    * A vector of ip4 nbr counters
389    */
390   vl_api_ip4_nbr_counter_t *counters;
391 } ip4_nbr_stats_ctx_t;
392
393 static adj_walk_rc_t
394 ip4_nbr_stats_cb (adj_index_t ai, void *arg)
395 {
396   vl_api_ip4_nbr_counter_t *vl_counter;
397   vlib_counter_t adj_counter;
398   ip4_nbr_stats_ctx_t *ctx;
399   ip_adjacency_t *adj;
400
401   ctx = arg;
402   vlib_get_combined_counter (&adjacency_counters, ai, &adj_counter);
403
404   if (0 != adj_counter.packets)
405     {
406       vec_add2 (ctx->counters, vl_counter, 1);
407       adj = adj_get (ai);
408
409       vl_counter->packets = clib_host_to_net_u64 (adj_counter.packets);
410       vl_counter->bytes = clib_host_to_net_u64 (adj_counter.bytes);
411       vl_counter->address = adj->sub_type.nbr.next_hop.ip4.as_u32;
412       vl_counter->link_type = adj->ia_link;
413     }
414   return (ADJ_WALK_RC_CONTINUE);
415 }
416
417 #define MIN(x,y) (((x)<(y))?(x):(y))
418
419 static void
420 ip4_nbr_ship (stats_main_t * sm, ip4_nbr_stats_ctx_t * ctx)
421 {
422   api_main_t *am = sm->api_main;
423   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
424   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
425   vl_api_vnet_ip4_nbr_counters_t *mp = 0;
426   int first = 0;
427
428   /*
429    * If the walk context has counters, which may be left over from the last
430    * suspend, then we continue from there.
431    */
432   while (0 != vec_len (ctx->counters))
433     {
434       u32 n_items = MIN (vec_len (ctx->counters),
435                          IP4_FIB_COUNTER_BATCH_SIZE);
436       u8 pause = 0;
437
438       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
439
440       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
441                                           (n_items *
442                                            sizeof
443                                            (vl_api_ip4_nbr_counter_t)));
444       mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_NBR_COUNTERS);
445       mp->count = ntohl (n_items);
446       mp->sw_if_index = ntohl (ctx->sw_if_index);
447       mp->begin = first;
448       first = 0;
449
450       /*
451        * copy the counters from the back of the context, then we can easily
452        * 'erase' them by resetting the vector length.
453        * The order we push the stats to the caller is not important.
454        */
455       clib_memcpy (mp->c,
456                    &ctx->counters[vec_len (ctx->counters) - n_items],
457                    n_items * sizeof (*ctx->counters));
458
459       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
460
461       /*
462        * send to the shm q
463        */
464       unix_shared_memory_queue_lock (q);
465       pause = unix_shared_memory_queue_is_full (q);
466
467       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
468       unix_shared_memory_queue_unlock (q);
469       dsunlock (sm);
470
471       if (pause)
472         ip46_fib_stats_delay (sm, 0 /* sec */ ,
473                               STATS_RELEASE_DELAY_NS);
474     }
475 }
476
477 static void
478 do_ip4_nbrs (stats_main_t * sm)
479 {
480   vnet_main_t *vnm = vnet_get_main ();
481   vnet_interface_main_t *im = &vnm->interface_main;
482   vnet_sw_interface_t *si;
483
484   ip4_nbr_stats_ctx_t ctx = {
485     .sw_if_index = 0,
486     .counters = NULL,
487   };
488
489   /* *INDENT-OFF* */
490   pool_foreach (si, im->sw_interfaces,
491   ({
492     /*
493      * update the interface we are now concerned with
494      */
495     ctx.sw_if_index = si->sw_if_index;
496
497     /*
498      * we are about to walk another interface, so we shouldn't have any pending
499      * stats to export.
500      */
501     ASSERT(ctx.counters == NULL);
502
503     /*
504      * visit each neighbour adjacency on the interface and collect
505      * its current stats.
506      * Because we hold the lock the walk is synchronous, so safe to routing
507      * updates. It's limited in work by the number of adjacenies on an
508      * interface, which is typically not huge.
509      */
510     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
511     adj_nbr_walk (si->sw_if_index,
512                   FIB_PROTOCOL_IP4,
513                   ip4_nbr_stats_cb,
514                   &ctx);
515     dsunlock (sm);
516
517     /*
518      * if this interface has some adjacencies with counters then ship them,
519      * else continue to the next interface.
520      */
521     if (NULL != ctx.counters)
522       {
523         ip4_nbr_ship(sm, &ctx);
524       }
525   }));
526   /* *INDENT-OFF* */
527 }
528
529 /**
530  * @brief The context passed when collecting adjacency counters
531  */
532 typedef struct ip6_nbr_stats_ctx_t_
533 {
534   /**
535    * The SW IF index all these adjs belong to
536    */
537   u32 sw_if_index;
538
539   /**
540    * A vector of ip6 nbr counters
541    */
542   vl_api_ip6_nbr_counter_t *counters;
543 } ip6_nbr_stats_ctx_t;
544
545 static adj_walk_rc_t
546 ip6_nbr_stats_cb (adj_index_t ai,
547                   void *arg)
548 {
549   vl_api_ip6_nbr_counter_t *vl_counter;
550   vlib_counter_t adj_counter;
551   ip6_nbr_stats_ctx_t *ctx;
552   ip_adjacency_t *adj;
553
554   ctx = arg;
555   vlib_get_combined_counter(&adjacency_counters, ai, &adj_counter);
556
557   if (0 != adj_counter.packets)
558     {
559       vec_add2(ctx->counters, vl_counter, 1);
560       adj = adj_get(ai);
561
562       vl_counter->packets = clib_host_to_net_u64(adj_counter.packets);
563       vl_counter->bytes   = clib_host_to_net_u64(adj_counter.bytes);
564       vl_counter->address[0] = adj->sub_type.nbr.next_hop.ip6.as_u64[0];
565       vl_counter->address[1] = adj->sub_type.nbr.next_hop.ip6.as_u64[1];
566       vl_counter->link_type = adj->ia_link;
567     }
568   return (ADJ_WALK_RC_CONTINUE);
569 }
570
571 #define MIN(x,y) (((x)<(y))?(x):(y))
572
573 static void
574 ip6_nbr_ship (stats_main_t * sm,
575               ip6_nbr_stats_ctx_t *ctx)
576 {
577   api_main_t *am = sm->api_main;
578   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
579   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
580   vl_api_vnet_ip6_nbr_counters_t *mp = 0;
581   int first = 0;
582
583   /*
584    * If the walk context has counters, which may be left over from the last
585    * suspend, then we continue from there.
586    */
587   while (0 != vec_len(ctx->counters))
588     {
589       u32 n_items = MIN (vec_len (ctx->counters),
590                          IP6_FIB_COUNTER_BATCH_SIZE);
591       u8 pause = 0;
592
593       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
594
595       mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) +
596                                           (n_items *
597                                            sizeof
598                                            (vl_api_ip6_nbr_counter_t)));
599       mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_NBR_COUNTERS);
600       mp->count = ntohl (n_items);
601       mp->sw_if_index = ntohl (ctx->sw_if_index);
602       mp->begin = first;
603       first = 0;
604
605       /*
606        * copy the counters from the back of the context, then we can easily
607        * 'erase' them by resetting the vector length.
608        * The order we push the stats to the caller is not important.
609        */
610       clib_memcpy (mp->c,
611                    &ctx->counters[vec_len (ctx->counters) - n_items],
612                    n_items * sizeof (*ctx->counters));
613
614       _vec_len (ctx->counters) = vec_len (ctx->counters) - n_items;
615
616       /*
617        * send to the shm q
618        */
619       unix_shared_memory_queue_lock (q);
620       pause = unix_shared_memory_queue_is_full (q);
621
622       vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
623       unix_shared_memory_queue_unlock (q);
624       dsunlock (sm);
625
626       if (pause)
627         ip46_fib_stats_delay (sm, 0 /* sec */ ,
628                               STATS_RELEASE_DELAY_NS);
629     }
630 }
631
632 static void
633 do_ip6_nbrs (stats_main_t * sm)
634 {
635   vnet_main_t *vnm = vnet_get_main ();
636   vnet_interface_main_t *im = &vnm->interface_main;
637   vnet_sw_interface_t *si;
638
639   ip6_nbr_stats_ctx_t ctx = {
640     .sw_if_index = 0,
641     .counters = NULL,
642   };
643
644   /* *INDENT-OFF* */
645   pool_foreach (si, im->sw_interfaces,
646   ({
647     /*
648      * update the interface we are now concerned with
649      */
650     ctx.sw_if_index = si->sw_if_index;
651
652     /*
653      * we are about to walk another interface, so we shouldn't have any pending
654      * stats to export.
655      */
656     ASSERT(ctx.counters == NULL);
657
658     /*
659      * visit each neighbour adjacency on the interface and collect
660      * its current stats.
661      * Because we hold the lock the walk is synchronous, so safe to routing
662      * updates. It's limited in work by the number of adjacenies on an
663      * interface, which is typically not huge.
664      */
665     dslock (sm, 0 /* release hint */ , 1 /* tag */ );
666     adj_nbr_walk (si->sw_if_index,
667                   FIB_PROTOCOL_IP6,
668                   ip6_nbr_stats_cb,
669                   &ctx);
670     dsunlock (sm);
671
672     /*
673      * if this interface has some adjacencies with counters then ship them,
674      * else continue to the next interface.
675      */
676     if (NULL != ctx.counters)
677       {
678         ip6_nbr_ship(sm, &ctx);
679       }
680   }));
681   /* *INDENT-OFF* */
682 }
683
684 static void
685 do_ip4_fibs (stats_main_t * sm)
686 {
687   ip4_main_t *im4 = &ip4_main;
688   api_main_t *am = sm->api_main;
689   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
690   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
691   ip4_route_t *r;
692   fib_table_t *fib;
693   ip4_fib_t *v4_fib;
694   do_ip46_fibs_t *do_fibs;
695   vl_api_vnet_ip4_fib_counters_t *mp = 0;
696   u32 items_this_message;
697   vl_api_ip4_fib_counter_t *ctrp = 0;
698   u32 start_at_fib_index = 0;
699   int i, j, k;
700
701   do_fibs = &sm->do_ip46_fibs;
702
703 again:
704   vec_reset_length (do_fibs->fibs);
705   /* *INDENT-OFF* */
706   pool_foreach (fib, im4->fibs,
707                 ({vec_add1(do_fibs->fibs,fib);}));
708
709   /* *INDENT-ON* */
710
711   for (j = 0; j < vec_len (do_fibs->fibs); j++)
712     {
713       fib = do_fibs->fibs[j];
714       /* We may have bailed out due to control-plane activity */
715       while ((fib - im4->fibs) < start_at_fib_index)
716         continue;
717
718       v4_fib = pool_elt_at_index (im4->v4_fibs, fib->ft_index);
719
720       if (mp == 0)
721         {
722           items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
723           mp = vl_msg_api_alloc_as_if_client
724             (sizeof (*mp) +
725              items_this_message * sizeof (vl_api_ip4_fib_counter_t));
726           mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
727           mp->count = 0;
728           mp->vrf_id = ntohl (fib->ft_table_id);
729           ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
730         }
731       else
732         {
733           /* happens if the last FIB was empty... */
734           ASSERT (mp->count == 0);
735           mp->vrf_id = ntohl (fib->ft_table_id);
736         }
737
738       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
739
740       vec_reset_length (do_fibs->ip4routes);
741       vec_reset_length (do_fibs->results);
742
743       for (i = 0; i < ARRAY_LEN (v4_fib->fib_entry_by_dst_address); i++)
744         {
745           uword *hash = v4_fib->fib_entry_by_dst_address[i];
746           hash_pair_t *p;
747           ip4_route_t x;
748
749           vec_reset_length (do_fibs->pvec);
750
751           x.address_length = i;
752
753           hash_foreach_pair (p, hash, (
754                                         {
755                                         vec_add1 (do_fibs->pvec, p);}
756                              ));
757           for (k = 0; k < vec_len (do_fibs->pvec); k++)
758             {
759               p = do_fibs->pvec[k];
760               x.address.data_u32 = p->key;
761               x.index = p->value[0];
762
763               vec_add1 (do_fibs->ip4routes, x);
764               if (sm->data_structure_lock->release_hint)
765                 {
766                   start_at_fib_index = fib - im4->fibs;
767                   dsunlock (sm);
768                   ip46_fib_stats_delay (sm, 0 /* sec */ ,
769                                         STATS_RELEASE_DELAY_NS);
770                   mp->count = 0;
771                   ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
772                   goto again;
773                 }
774             }
775         }
776
777       vec_foreach (r, do_fibs->ip4routes)
778       {
779         vlib_counter_t c;
780         const dpo_id_t *dpo_id;
781         u32 index;
782
783         dpo_id = fib_entry_contribute_ip_forwarding (r->index);
784         index = (u32) dpo_id->dpoi_index;
785
786         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
787                                    index, &c);
788         /*
789          * If it has actually
790          * seen at least one packet, send it.
791          */
792         if (c.packets > 0)
793           {
794
795             /* already in net byte order */
796             ctrp->address = r->address.as_u32;
797             ctrp->address_length = r->address_length;
798             ctrp->packets = clib_host_to_net_u64 (c.packets);
799             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
800             mp->count++;
801             ctrp++;
802
803             if (mp->count == items_this_message)
804               {
805                 mp->count = htonl (items_this_message);
806                 /*
807                  * If the main thread's input queue is stuffed,
808                  * drop the data structure lock (which the main thread
809                  * may want), and take a pause.
810                  */
811                 unix_shared_memory_queue_lock (q);
812                 if (unix_shared_memory_queue_is_full (q))
813                   {
814                     dsunlock (sm);
815                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
816                     unix_shared_memory_queue_unlock (q);
817                     mp = 0;
818                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
819                                           STATS_RELEASE_DELAY_NS);
820                     goto again;
821                   }
822                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
823                 unix_shared_memory_queue_unlock (q);
824
825                 items_this_message = IP4_FIB_COUNTER_BATCH_SIZE;
826                 mp = vl_msg_api_alloc_as_if_client
827                   (sizeof (*mp) +
828                    items_this_message * sizeof (vl_api_ip4_fib_counter_t));
829                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP4_FIB_COUNTERS);
830                 mp->count = 0;
831                 mp->vrf_id = ntohl (fib->ft_table_id);
832                 ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
833               }
834           }                     /* for each (mp or single) adj */
835         if (sm->data_structure_lock->release_hint)
836           {
837             start_at_fib_index = fib - im4->fibs;
838             dsunlock (sm);
839             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
840             mp->count = 0;
841             ctrp = (vl_api_ip4_fib_counter_t *) mp->c;
842             goto again;
843           }
844       }                         /* vec_foreach (routes) */
845
846       dsunlock (sm);
847
848       /* Flush any data from this fib */
849       if (mp->count)
850         {
851           mp->count = htonl (mp->count);
852           vl_msg_api_send_shmem (q, (u8 *) & mp);
853           mp = 0;
854         }
855     }
856
857   /* If e.g. the last FIB had no reportable routes, free the buffer */
858   if (mp)
859     vl_msg_api_free (mp);
860 }
861
862 typedef struct
863 {
864   u32 fib_index;
865   ip6_route_t **routep;
866   stats_main_t *sm;
867 } add_routes_in_fib_arg_t;
868
869 static void
870 add_routes_in_fib (BVT (clib_bihash_kv) * kvp, void *arg)
871 {
872   add_routes_in_fib_arg_t *ap = arg;
873   stats_main_t *sm = ap->sm;
874
875   if (sm->data_structure_lock->release_hint)
876     clib_longjmp (&sm->jmp_buf, 1);
877
878   if (kvp->key[2] >> 32 == ap->fib_index)
879     {
880       ip6_address_t *addr;
881       ip6_route_t *r;
882       addr = (ip6_address_t *) kvp;
883       vec_add2 (*ap->routep, r, 1);
884       r->address = addr[0];
885       r->address_length = kvp->key[2] & 0xFF;
886       r->index = kvp->value;
887     }
888 }
889
890 static void
891 do_ip6_fibs (stats_main_t * sm)
892 {
893   ip6_main_t *im6 = &ip6_main;
894   api_main_t *am = sm->api_main;
895   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
896   unix_shared_memory_queue_t *q = shmem_hdr->vl_input_queue;
897   ip6_route_t *r;
898   fib_table_t *fib;
899   do_ip46_fibs_t *do_fibs;
900   vl_api_vnet_ip6_fib_counters_t *mp = 0;
901   u32 items_this_message;
902   vl_api_ip6_fib_counter_t *ctrp = 0;
903   u32 start_at_fib_index = 0;
904   BVT (clib_bihash) * h = &im6->ip6_table[IP6_FIB_TABLE_FWDING].ip6_hash;
905   add_routes_in_fib_arg_t _a, *a = &_a;
906   int i;
907
908   do_fibs = &sm->do_ip46_fibs;
909 again:
910   vec_reset_length (do_fibs->fibs);
911   /* *INDENT-OFF* */
912   pool_foreach (fib, im6->fibs,
913                 ({vec_add1(do_fibs->fibs,fib);}));
914   /* *INDENT-ON* */
915
916
917   for (i = 0; i < vec_len (do_fibs->fibs); i++)
918     {
919       fib = do_fibs->fibs[i];
920       /* We may have bailed out due to control-plane activity */
921       while ((fib - im6->fibs) < start_at_fib_index)
922         continue;
923
924       if (mp == 0)
925         {
926           items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
927           mp = vl_msg_api_alloc_as_if_client
928             (sizeof (*mp) +
929              items_this_message * sizeof (vl_api_ip6_fib_counter_t));
930           mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
931           mp->count = 0;
932           mp->vrf_id = ntohl (fib->ft_table_id);
933           ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
934         }
935
936       dslock (sm, 0 /* release hint */ , 1 /* tag */ );
937
938       vec_reset_length (do_fibs->ip6routes);
939       vec_reset_length (do_fibs->results);
940
941       a->fib_index = fib - im6->fibs;
942       a->routep = &do_fibs->ip6routes;
943       a->sm = sm;
944
945       if (clib_setjmp (&sm->jmp_buf, 0) == 0)
946         {
947           start_at_fib_index = fib - im6->fibs;
948           BV (clib_bihash_foreach_key_value_pair) (h, add_routes_in_fib, a);
949         }
950       else
951         {
952           dsunlock (sm);
953           ip46_fib_stats_delay (sm, 0 /* sec */ ,
954                                 STATS_RELEASE_DELAY_NS);
955           mp->count = 0;
956           ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
957           goto again;
958         }
959
960       vec_foreach (r, do_fibs->ip6routes)
961       {
962         vlib_counter_t c;
963
964         vlib_get_combined_counter (&load_balance_main.lbm_to_counters,
965                                    r->index, &c);
966         /*
967          * If it has actually
968          * seen at least one packet, send it.
969          */
970         if (c.packets > 0)
971           {
972             /* already in net byte order */
973             ctrp->address[0] = r->address.as_u64[0];
974             ctrp->address[1] = r->address.as_u64[1];
975             ctrp->address_length = (u8) r->address_length;
976             ctrp->packets = clib_host_to_net_u64 (c.packets);
977             ctrp->bytes = clib_host_to_net_u64 (c.bytes);
978             mp->count++;
979             ctrp++;
980
981             if (mp->count == items_this_message)
982               {
983                 mp->count = htonl (items_this_message);
984                 /*
985                  * If the main thread's input queue is stuffed,
986                  * drop the data structure lock (which the main thread
987                  * may want), and take a pause.
988                  */
989                 unix_shared_memory_queue_lock (q);
990                 if (unix_shared_memory_queue_is_full (q))
991                   {
992                     dsunlock (sm);
993                     vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
994                     unix_shared_memory_queue_unlock (q);
995                     mp = 0;
996                     ip46_fib_stats_delay (sm, 0 /* sec */ ,
997                                           STATS_RELEASE_DELAY_NS);
998                     goto again;
999                   }
1000                 vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1001                 unix_shared_memory_queue_unlock (q);
1002
1003                 items_this_message = IP6_FIB_COUNTER_BATCH_SIZE;
1004                 mp = vl_msg_api_alloc_as_if_client
1005                   (sizeof (*mp) +
1006                    items_this_message * sizeof (vl_api_ip6_fib_counter_t));
1007                 mp->_vl_msg_id = ntohs (VL_API_VNET_IP6_FIB_COUNTERS);
1008                 mp->count = 0;
1009                 mp->vrf_id = ntohl (fib->ft_table_id);
1010                 ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1011               }
1012           }
1013
1014         if (sm->data_structure_lock->release_hint)
1015           {
1016             start_at_fib_index = fib - im6->fibs;
1017             dsunlock (sm);
1018             ip46_fib_stats_delay (sm, 0 /* sec */ , STATS_RELEASE_DELAY_NS);
1019             mp->count = 0;
1020             ctrp = (vl_api_ip6_fib_counter_t *) mp->c;
1021             goto again;
1022           }
1023       }                         /* vec_foreach (routes) */
1024
1025       dsunlock (sm);
1026
1027       /* Flush any data from this fib */
1028       if (mp->count)
1029         {
1030           mp->count = htonl (mp->count);
1031           vl_msg_api_send_shmem (q, (u8 *) & mp);
1032           mp = 0;
1033         }
1034     }
1035
1036   /* If e.g. the last FIB had no reportable routes, free the buffer */
1037   if (mp)
1038     vl_msg_api_free (mp);
1039 }
1040
1041 static void
1042 stats_thread_fn (void *arg)
1043 {
1044   stats_main_t *sm = &stats_main;
1045   vlib_worker_thread_t *w = (vlib_worker_thread_t *) arg;
1046   vlib_thread_main_t *tm = vlib_get_thread_main ();
1047
1048   /* stats thread wants no signals. */
1049   {
1050     sigset_t s;
1051     sigfillset (&s);
1052     pthread_sigmask (SIG_SETMASK, &s, 0);
1053   }
1054
1055   if (vec_len (tm->thread_prefix))
1056     vlib_set_thread_name ((char *)
1057                           format (0, "%v_stats%c", tm->thread_prefix, '\0'));
1058
1059   clib_mem_set_heap (w->thread_mheap);
1060
1061   while (1)
1062     {
1063       /* 10 second poll interval */
1064       ip46_fib_stats_delay (sm, 10 /* secs */ , 0 /* nsec */ );
1065
1066       if (!(sm->enable_poller))
1067         continue;
1068       do_simple_interface_counters (sm);
1069       do_combined_interface_counters (sm);
1070       do_ip4_fibs (sm);
1071       do_ip6_fibs (sm);
1072       do_ip4_nbrs (sm);
1073       do_ip6_nbrs (sm);
1074     }
1075 }
1076
1077 static void
1078   vl_api_vnet_interface_simple_counters_t_handler
1079   (vl_api_vnet_interface_simple_counters_t * mp)
1080 {
1081   vpe_client_stats_registration_t *reg;
1082   stats_main_t *sm = &stats_main;
1083   unix_shared_memory_queue_t *q, *q_prev = NULL;
1084   vl_api_vnet_interface_simple_counters_t *mp_copy = NULL;
1085   u32 mp_size;
1086   int i;
1087
1088   mp_size = sizeof (*mp) + (ntohl (mp->count) * sizeof (u64));
1089
1090   /* *INDENT-OFF* */
1091   vec_reset_length(sm->regs);
1092   pool_foreach(reg, sm->stats_registrations,
1093                ({
1094              vec_add1(sm->regs,reg);
1095                }));
1096   /* *INDENT-ON* */
1097   for (i = 0; i < vec_len (sm->regs); i++)
1098     {
1099       reg = sm->regs[i];
1100       if (reg->stats_registrations & INTERFACE_SIMPLE_COUNTERS)
1101         {
1102           q = vl_api_client_index_to_input_queue (reg->client.client_index);
1103           if (q)
1104             {
1105               if (q_prev && (q_prev->cursize < q_prev->maxsize))
1106                 {
1107                   mp_copy = vl_msg_api_alloc_as_if_client (mp_size);
1108                   clib_memcpy (mp_copy, mp, mp_size);
1109                   vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1110                   mp = mp_copy;
1111                 }
1112               q_prev = q;
1113             }
1114         }
1115     }
1116 #if STATS_DEBUG > 0
1117   fformat (stdout, "%U\n", format_vnet_simple_counters, mp);
1118 #endif
1119
1120   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1121     {
1122       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1123     }
1124   else
1125     {
1126       vl_msg_api_free (mp);
1127     }
1128 }
1129
1130 static void
1131   vl_api_vnet_interface_combined_counters_t_handler
1132   (vl_api_vnet_interface_combined_counters_t * mp)
1133 {
1134   vpe_client_stats_registration_t *reg;
1135   stats_main_t *sm = &stats_main;
1136   unix_shared_memory_queue_t *q, *q_prev = NULL;
1137   vl_api_vnet_interface_combined_counters_t *mp_copy = NULL;
1138   u32 mp_size;
1139
1140   mp_size = sizeof (*mp) + (ntohl (mp->count) * sizeof (vlib_counter_t));
1141
1142   /* *INDENT-OFF* */
1143   pool_foreach(reg, sm->stats_registrations,
1144                ({
1145              if (reg->stats_registrations & INTERFACE_COMBINED_COUNTERS)
1146                {
1147                  q = vl_api_client_index_to_input_queue (reg->client.client_index);
1148                  if (q)
1149                    {
1150                      if (q_prev && (q_prev->cursize < q_prev->maxsize))
1151                        {
1152                          mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1153                          clib_memcpy(mp_copy, mp, mp_size);
1154                          vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1155                          mp = mp_copy;
1156                        }
1157                      q_prev = q;
1158                    }
1159                }
1160                }));
1161   /* *INDENT-ON* */
1162
1163 #if STATS_DEBUG > 0
1164   fformat (stdout, "%U\n", format_vnet_combined_counters, mp);
1165 #endif
1166
1167   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1168     {
1169       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1170     }
1171   else
1172     {
1173       vl_msg_api_free (mp);
1174     }
1175 }
1176
1177 static void
1178 vl_api_vnet_ip4_fib_counters_t_handler (vl_api_vnet_ip4_fib_counters_t * mp)
1179 {
1180   vpe_client_stats_registration_t *reg;
1181   stats_main_t *sm = &stats_main;
1182   unix_shared_memory_queue_t *q, *q_prev = NULL;
1183   vl_api_vnet_ip4_fib_counters_t *mp_copy = NULL;
1184   u32 mp_size;
1185
1186   mp_size = sizeof (*mp_copy) +
1187     ntohl (mp->count) * sizeof (vl_api_ip4_fib_counter_t);
1188
1189   /* *INDENT-OFF* */
1190   pool_foreach(reg, sm->stats_registrations,
1191   ({
1192     if (reg->stats_registrations & IP4_FIB_COUNTERS)
1193       {
1194         q = vl_api_client_index_to_input_queue (reg->client.client_index);
1195         if (q)
1196           {
1197             if (q_prev && (q_prev->cursize < q_prev->maxsize))
1198               {
1199                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1200                 clib_memcpy(mp_copy, mp, mp_size);
1201                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1202                 mp = mp_copy;
1203               }
1204             q_prev = q;
1205           }
1206       }
1207   }));
1208   /* *INDENT-ON* */
1209   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1210     {
1211       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1212     }
1213   else
1214     {
1215       vl_msg_api_free (mp);
1216     }
1217 }
1218
1219 static void
1220 vl_api_vnet_ip4_nbr_counters_t_handler (vl_api_vnet_ip4_nbr_counters_t * mp)
1221 {
1222   vpe_client_stats_registration_t *reg;
1223   stats_main_t *sm = &stats_main;
1224   unix_shared_memory_queue_t *q, *q_prev = NULL;
1225   vl_api_vnet_ip4_nbr_counters_t *mp_copy = NULL;
1226   u32 mp_size;
1227
1228   mp_size = sizeof (*mp_copy) +
1229     ntohl (mp->count) * sizeof (vl_api_ip4_nbr_counter_t);
1230
1231   /* *INDENT-OFF* */
1232   pool_foreach(reg, sm->stats_registrations,
1233   ({
1234     if (reg->stats_registrations & IP4_NBR_COUNTERS)
1235       {
1236         q = vl_api_client_index_to_input_queue (reg->client.client_index);
1237         if (q)
1238           {
1239             if (q_prev && (q_prev->cursize < q_prev->maxsize))
1240               {
1241                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1242                 clib_memcpy(mp_copy, mp, mp_size);
1243                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1244                 mp = mp_copy;
1245               }
1246             q_prev = q;
1247           }
1248       }
1249   }));
1250   /* *INDENT-ON* */
1251   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1252     {
1253       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1254     }
1255   else
1256     {
1257       vl_msg_api_free (mp);
1258     }
1259 }
1260
1261 static void
1262 vl_api_vnet_ip6_fib_counters_t_handler (vl_api_vnet_ip6_fib_counters_t * mp)
1263 {
1264   vpe_client_stats_registration_t *reg;
1265   stats_main_t *sm = &stats_main;
1266   unix_shared_memory_queue_t *q, *q_prev = NULL;
1267   vl_api_vnet_ip6_fib_counters_t *mp_copy = NULL;
1268   u32 mp_size;
1269
1270   mp_size = sizeof (*mp_copy) +
1271     ntohl (mp->count) * sizeof (vl_api_ip6_fib_counter_t);
1272
1273   /* *INDENT-OFF* */
1274   pool_foreach(reg, sm->stats_registrations,
1275   ({
1276     if (reg->stats_registrations & IP6_FIB_COUNTERS)
1277       {
1278         q = vl_api_client_index_to_input_queue (reg->client.client_index);
1279         if (q)
1280           {
1281             if (q_prev && (q_prev->cursize < q_prev->maxsize))
1282               {
1283                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1284                 clib_memcpy(mp_copy, mp, mp_size);
1285                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1286                 mp = mp_copy;
1287               }
1288             q_prev = q;
1289           }
1290       }
1291       }));
1292   /* *INDENT-ON* */
1293   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1294     {
1295       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1296     }
1297   else
1298     {
1299       vl_msg_api_free (mp);
1300     }
1301 }
1302
1303 static void
1304 vl_api_vnet_ip6_nbr_counters_t_handler (vl_api_vnet_ip6_nbr_counters_t * mp)
1305 {
1306   vpe_client_stats_registration_t *reg;
1307   stats_main_t *sm = &stats_main;
1308   unix_shared_memory_queue_t *q, *q_prev = NULL;
1309   vl_api_vnet_ip6_nbr_counters_t *mp_copy = NULL;
1310   u32 mp_size;
1311
1312   mp_size = sizeof (*mp_copy) +
1313     ntohl (mp->count) * sizeof (vl_api_ip6_nbr_counter_t);
1314
1315   /* *INDENT-OFF* */
1316   pool_foreach(reg, sm->stats_registrations,
1317   ({
1318     if (reg->stats_registrations & IP6_NBR_COUNTERS)
1319       {
1320         q = vl_api_client_index_to_input_queue (reg->client.client_index);
1321         if (q)
1322           {
1323             if (q_prev && (q_prev->cursize < q_prev->maxsize))
1324               {
1325                 mp_copy = vl_msg_api_alloc_as_if_client(mp_size);
1326                 clib_memcpy(mp_copy, mp, mp_size);
1327                 vl_msg_api_send_shmem (q_prev, (u8 *)&mp);
1328                 mp = mp_copy;
1329               }
1330             q_prev = q;
1331           }
1332       }
1333   }));
1334   /* *INDENT-ON* */
1335   if (q_prev && (q_prev->cursize < q_prev->maxsize))
1336     {
1337       vl_msg_api_send_shmem (q_prev, (u8 *) & mp);
1338     }
1339   else
1340     {
1341       vl_msg_api_free (mp);
1342     }
1343 }
1344
1345 static void
1346 vl_api_want_stats_t_handler (vl_api_want_stats_t * mp)
1347 {
1348   stats_main_t *sm = &stats_main;
1349   vpe_client_stats_registration_t *rp;
1350   vl_api_want_stats_reply_t *rmp;
1351   uword *p;
1352   i32 retval = 0;
1353   unix_shared_memory_queue_t *q;
1354
1355   p = hash_get (sm->stats_registration_hash, mp->client_index);
1356   if (p)
1357     {
1358       if (mp->enable_disable)
1359         {
1360           clib_warning ("pid %d: already enabled...", mp->pid);
1361           retval = -2;
1362           goto reply;
1363         }
1364       else
1365         {
1366           rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1367           pool_put (sm->stats_registrations, rp);
1368           hash_unset (sm->stats_registration_hash, mp->client_index);
1369           goto reply;
1370         }
1371     }
1372   if (mp->enable_disable == 0)
1373     {
1374       clib_warning ("pid %d: already disabled...", mp->pid);
1375       retval = -3;
1376       goto reply;
1377     }
1378   pool_get (sm->stats_registrations, rp);
1379   rp->client.client_index = mp->client_index;
1380   rp->client.client_pid = mp->pid;
1381   rp->stats_registrations |= INTERFACE_SIMPLE_COUNTERS;
1382   rp->stats_registrations |= INTERFACE_COMBINED_COUNTERS;
1383   rp->stats_registrations |= IP4_FIB_COUNTERS;
1384   rp->stats_registrations |= IP4_NBR_COUNTERS;
1385   rp->stats_registrations |= IP6_FIB_COUNTERS;
1386   rp->stats_registrations |= IP6_NBR_COUNTERS;
1387
1388   hash_set (sm->stats_registration_hash, rp->client.client_index,
1389             rp - sm->stats_registrations);
1390
1391 reply:
1392   if (pool_elts (sm->stats_registrations))
1393     sm->enable_poller = 1;
1394   else
1395     sm->enable_poller = 0;
1396
1397   q = vl_api_client_index_to_input_queue (mp->client_index);
1398
1399   if (!q)
1400     return;
1401
1402   rmp = vl_msg_api_alloc (sizeof (*rmp));
1403   rmp->_vl_msg_id = ntohs (VL_API_WANT_STATS_REPLY);
1404   rmp->context = mp->context;
1405   rmp->retval = retval;
1406
1407   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1408 }
1409
1410 static void
1411   vl_api_want_interface_simple_stats_t_handler
1412   (vl_api_want_interface_simple_stats_t * mp)
1413 {
1414   stats_main_t *sm = &stats_main;
1415   vpe_client_stats_registration_t *rp;
1416   vl_api_want_interface_simple_stats_reply_t *rmp;
1417   uword *p;
1418   i32 retval = 0;
1419   unix_shared_memory_queue_t *q;
1420
1421   p = hash_get (sm->stats_registration_hash, mp->client_index);
1422
1423   /* Disable case */
1424   if (mp->enable_disable == 0)
1425     {
1426       if (!p)                   // No client to disable
1427         {
1428           clib_warning ("pid %d: already disabled for stats...", mp->pid);
1429           retval = 0;
1430           goto reply;
1431         }
1432
1433       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1434       if (!rp->stats_registrations & INTERFACE_SIMPLE_COUNTERS) // Client but doesn't want this.
1435         {
1436           clib_warning
1437             ("pid %d: already disabled for interface simple stats...",
1438              mp->pid);
1439           retval = 0;
1440           goto reply;
1441         }
1442       else
1443         {
1444           rp->stats_registrations &= ~(INTERFACE_SIMPLE_COUNTERS);      // Clear flag
1445           if (rp->stats_registrations == 0)     // Client isn't listening to anything else
1446             {
1447               pool_put (sm->stats_registrations, rp);
1448               hash_unset (sm->stats_registration_hash, mp->client_index);
1449             }
1450           goto reply;
1451         }
1452     }
1453   /* Enable case */
1454   /* Get client from pool */
1455   if (p)
1456     rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1457
1458   if (!p || !rp)                // Doesn't exist, make a new entry
1459     {
1460       pool_get (sm->stats_registrations, rp);
1461       rp->client.client_index = mp->client_index;
1462       rp->client.client_pid = mp->pid;
1463     }
1464   rp->stats_registrations |= INTERFACE_SIMPLE_COUNTERS;
1465   hash_set (sm->stats_registration_hash, rp->client.client_index,
1466             rp - sm->stats_registrations);
1467
1468 reply:
1469   if (pool_elts (sm->stats_registrations))      // Someone wants something, somewhere so enable globally for now.
1470     sm->enable_poller = 1;
1471   else
1472     sm->enable_poller = 0;
1473
1474   q = vl_api_client_index_to_input_queue (mp->client_index);
1475
1476   if (!q)
1477     return;
1478
1479   rmp = vl_msg_api_alloc (sizeof (*rmp));
1480   rmp->_vl_msg_id = ntohs (VL_API_WANT_INTERFACE_SIMPLE_STATS_REPLY);
1481   rmp->context = mp->context;
1482   rmp->retval = retval;
1483
1484   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1485 }
1486
1487 static void
1488   vl_api_want_interface_combined_stats_t_handler
1489   (vl_api_want_interface_combined_stats_t * mp)
1490 {
1491   stats_main_t *sm = &stats_main;
1492   vpe_client_stats_registration_t *rp;
1493   vl_api_want_interface_combined_stats_reply_t *rmp;
1494   uword *p;
1495   i32 retval = 0;
1496   unix_shared_memory_queue_t *q;
1497
1498   p = hash_get (sm->stats_registration_hash, mp->client_index);
1499
1500   /* Disable case */
1501   if (mp->enable_disable == 0)
1502     {
1503       if (!p)                   // No client to disable
1504         {
1505           clib_warning ("pid %d: already disabled for stats...", mp->pid);
1506           retval = 0;
1507           goto reply;
1508         }
1509
1510       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1511       if (!(rp->stats_registrations & INTERFACE_COMBINED_COUNTERS))     // Client but doesn't want this.
1512         {
1513           clib_warning
1514             ("pid %d: already disabled for interface COMBINED stats...",
1515              mp->pid);
1516           retval = 0;
1517           goto reply;
1518         }
1519       else
1520         {
1521           rp->stats_registrations &= ~(INTERFACE_COMBINED_COUNTERS);    // Clear flag
1522           if (rp->stats_registrations == 0)     // Client isn't listening to anything else
1523             {
1524               pool_put (sm->stats_registrations, rp);
1525               hash_unset (sm->stats_registration_hash, mp->client_index);
1526             }
1527           goto reply;
1528         }
1529     }
1530   /* Enable case */
1531   /* Get client from pool */
1532   if (p)
1533     rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1534
1535   if (!p || !rp)                // Doesn't exist, make a new entry
1536     {
1537       pool_get (sm->stats_registrations, rp);
1538       rp->client.client_index = mp->client_index;
1539       rp->client.client_pid = mp->pid;
1540     }
1541   rp->stats_registrations |= INTERFACE_COMBINED_COUNTERS;
1542   hash_set (sm->stats_registration_hash, rp->client.client_index,
1543             rp - sm->stats_registrations);
1544
1545 reply:
1546   if (pool_elts (sm->stats_registrations))      // Someone wants something, somewhere so enable globally for now.
1547     sm->enable_poller = 1;
1548   else
1549     sm->enable_poller = 0;
1550
1551   q = vl_api_client_index_to_input_queue (mp->client_index);
1552
1553   if (!q)
1554     return;
1555
1556   rmp = vl_msg_api_alloc (sizeof (*rmp));
1557   rmp->_vl_msg_id = ntohs (VL_API_WANT_INTERFACE_COMBINED_STATS_REPLY);
1558   rmp->context = mp->context;
1559   rmp->retval = retval;
1560
1561   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1562 }
1563
1564 static void
1565 vl_api_want_ip4_fib_stats_t_handler (vl_api_want_ip4_fib_stats_t * mp)
1566 {
1567   stats_main_t *sm = &stats_main;
1568   vpe_client_stats_registration_t *rp;
1569   vl_api_want_ip4_fib_stats_reply_t *rmp;
1570   uword *p;
1571   i32 retval = 0;
1572   unix_shared_memory_queue_t *q;
1573
1574   p = hash_get (sm->stats_registration_hash, mp->client_index);
1575
1576   /* Disable case */
1577   /*
1578      $$$ FIXME: need std return codes. Still undecided if enabling already
1579      enabled (and similar for disabled) is really a -'ve error condition or
1580      if 0 is sufficient
1581    */
1582   if (mp->enable_disable == 0)
1583     {
1584       if (!p)                   // No client to disable
1585         {
1586           clib_warning ("pid %d: already disabled for stats...", mp->pid);
1587           retval = -3;
1588           goto reply;
1589         }
1590
1591       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1592       if (!(rp->stats_registrations & IP4_FIB_COUNTERS))        // Client but doesn't want this.
1593         {
1594           clib_warning ("pid %d: already disabled for interface ip4 fib...",
1595                         mp->pid);
1596           retval = -2;
1597           goto reply;
1598         }
1599       else
1600         {
1601           rp->stats_registrations &= ~(IP4_FIB_COUNTERS);       // Clear flag
1602           if (rp->stats_registrations == 0)     // Client isn't listening to anything else
1603             {
1604               pool_put (sm->stats_registrations, rp);
1605               hash_unset (sm->stats_registration_hash, mp->client_index);
1606             }
1607           goto reply;
1608         }
1609     }
1610   /* Enable case */
1611   /* Get client from pool */
1612   if (p)
1613     rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1614
1615   if (!p || !rp)                // Doesn't exist, make a new entry
1616     {
1617       pool_get (sm->stats_registrations, rp);
1618       rp->client.client_index = mp->client_index;
1619       rp->client.client_pid = mp->pid;
1620     }
1621   rp->stats_registrations |= IP4_FIB_COUNTERS;
1622   hash_set (sm->stats_registration_hash, rp->client.client_index,
1623             rp - sm->stats_registrations);
1624
1625 reply:
1626   if (pool_elts (sm->stats_registrations))      // Someone wants something, somewhere so enable globally for now.
1627     sm->enable_poller = 1;
1628   else
1629     sm->enable_poller = 0;
1630
1631   q = vl_api_client_index_to_input_queue (mp->client_index);
1632
1633   if (!q)
1634     return;
1635
1636   rmp = vl_msg_api_alloc (sizeof (*rmp));
1637   rmp->_vl_msg_id = ntohs (VL_API_WANT_IP4_FIB_STATS_REPLY);
1638   rmp->context = mp->context;
1639   rmp->retval = retval;
1640
1641   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1642 }
1643
1644 static void
1645 vl_api_want_ip6_fib_stats_t_handler (vl_api_want_ip6_fib_stats_t * mp)
1646 {
1647   stats_main_t *sm = &stats_main;
1648   vpe_client_stats_registration_t *rp;
1649   vl_api_want_ip6_fib_stats_reply_t *rmp;
1650   uword *p;
1651   i32 retval = 0;
1652   unix_shared_memory_queue_t *q;
1653
1654   p = hash_get (sm->stats_registration_hash, mp->client_index);
1655
1656   /* Disable case */
1657   /*
1658      $$$ FIXME: need std return codes. Still undecided if enabling already
1659      enabled (and similar for disabled) is really a -'ve error condition or
1660      if 0 is sufficient
1661    */
1662   if (mp->enable_disable == 0)
1663     {
1664       if (!p)                   // No client to disable
1665         {
1666           clib_warning ("pid %d: already disabled for stats...", mp->pid);
1667           retval = -3;
1668           goto reply;
1669         }
1670
1671       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1672       if (!(rp->stats_registrations & IP6_FIB_COUNTERS))        // Client but doesn't want this.
1673         {
1674           clib_warning ("pid %d: already disabled for interface ip6 fib...",
1675                         mp->pid);
1676           retval = -2;
1677           goto reply;
1678         }
1679       else
1680         {
1681           rp->stats_registrations &= ~(IP6_FIB_COUNTERS);       // Clear flag
1682           if (rp->stats_registrations == 0)     // Client isn't listening to anything else
1683             {
1684               pool_put (sm->stats_registrations, rp);
1685               hash_unset (sm->stats_registration_hash, mp->client_index);
1686             }
1687           goto reply;
1688         }
1689     }
1690   /* Enable case */
1691   /* Get client from pool */
1692   if (p)
1693     rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1694
1695   if (!p || !rp)                // Doesn't exist, make a new entry
1696     {
1697       pool_get (sm->stats_registrations, rp);
1698       rp->client.client_index = mp->client_index;
1699       rp->client.client_pid = mp->pid;
1700     }
1701   rp->stats_registrations |= IP6_FIB_COUNTERS;
1702   hash_set (sm->stats_registration_hash, rp->client.client_index,
1703             rp - sm->stats_registrations);
1704
1705 reply:
1706   if (pool_elts (sm->stats_registrations))      // Someone wants something, somewhere so enable globally for now.
1707     sm->enable_poller = 1;
1708   else
1709     sm->enable_poller = 0;
1710
1711   q = vl_api_client_index_to_input_queue (mp->client_index);
1712
1713   if (!q)
1714     return;
1715
1716   rmp = vl_msg_api_alloc (sizeof (*rmp));
1717   rmp->_vl_msg_id = ntohs (VL_API_WANT_IP6_FIB_STATS_REPLY);
1718   rmp->context = mp->context;
1719   rmp->retval = retval;
1720
1721   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1722 }
1723
1724 /* FIXME - NBR stats broken - this will be fixed in subsequent patch */
1725 static void
1726 vl_api_want_ip4_nbr_stats_t_handler (vl_api_want_ip4_nbr_stats_t * mp)
1727 {
1728 }
1729
1730 static void
1731 vl_api_want_ip6_nbr_stats_t_handler (vl_api_want_ip6_nbr_stats_t * mp)
1732 {
1733 }
1734
1735 static void
1736 vl_api_vnet_get_summary_stats_t_handler (vl_api_vnet_get_summary_stats_t * mp)
1737 {
1738   stats_main_t *sm = &stats_main;
1739   vnet_interface_main_t *im = sm->interface_main;
1740   vl_api_vnet_get_summary_stats_reply_t *rmp;
1741   vlib_combined_counter_main_t *cm;
1742   vlib_counter_t v;
1743   int i, which;
1744   u64 total_pkts[VLIB_N_RX_TX];
1745   u64 total_bytes[VLIB_N_RX_TX];
1746
1747   unix_shared_memory_queue_t *q =
1748     vl_api_client_index_to_input_queue (mp->client_index);
1749
1750   if (!q)
1751     return;
1752
1753   rmp = vl_msg_api_alloc (sizeof (*rmp));
1754   rmp->_vl_msg_id = ntohs (VL_API_VNET_GET_SUMMARY_STATS_REPLY);
1755   rmp->context = mp->context;
1756   rmp->retval = 0;
1757
1758   memset (total_pkts, 0, sizeof (total_pkts));
1759   memset (total_bytes, 0, sizeof (total_bytes));
1760
1761   vnet_interface_counter_lock (im);
1762
1763   vec_foreach (cm, im->combined_sw_if_counters)
1764   {
1765     which = cm - im->combined_sw_if_counters;
1766
1767     for (i = 0; i < vlib_combined_counter_n_counters (cm); i++)
1768       {
1769         vlib_get_combined_counter (cm, i, &v);
1770         total_pkts[which] += v.packets;
1771         total_bytes[which] += v.bytes;
1772       }
1773   }
1774   vnet_interface_counter_unlock (im);
1775
1776   rmp->total_pkts[VLIB_RX] = clib_host_to_net_u64 (total_pkts[VLIB_RX]);
1777   rmp->total_bytes[VLIB_RX] = clib_host_to_net_u64 (total_bytes[VLIB_RX]);
1778   rmp->total_pkts[VLIB_TX] = clib_host_to_net_u64 (total_pkts[VLIB_TX]);
1779   rmp->total_bytes[VLIB_TX] = clib_host_to_net_u64 (total_bytes[VLIB_TX]);
1780   rmp->vector_rate =
1781     clib_host_to_net_u64 (vlib_last_vector_length_per_node (sm->vlib_main));
1782
1783   vl_msg_api_send_shmem (q, (u8 *) & rmp);
1784 }
1785
1786 int
1787 stats_memclnt_delete_callback (u32 client_index)
1788 {
1789   vpe_client_stats_registration_t *rp;
1790   stats_main_t *sm = &stats_main;
1791   uword *p;
1792
1793   p = hash_get (sm->stats_registration_hash, client_index);
1794   if (p)
1795     {
1796       rp = pool_elt_at_index (sm->stats_registrations, p[0]);
1797       pool_put (sm->stats_registrations, rp);
1798       hash_unset (sm->stats_registration_hash, client_index);
1799     }
1800
1801   return 0;
1802 }
1803
1804 #define vl_api_vnet_interface_simple_counters_t_endian vl_noop_handler
1805 #define vl_api_vnet_interface_simple_counters_t_print vl_noop_handler
1806 #define vl_api_vnet_interface_combined_counters_t_endian vl_noop_handler
1807 #define vl_api_vnet_interface_combined_counters_t_print vl_noop_handler
1808 #define vl_api_vnet_ip4_fib_counters_t_endian vl_noop_handler
1809 #define vl_api_vnet_ip4_fib_counters_t_print vl_noop_handler
1810 #define vl_api_vnet_ip6_fib_counters_t_endian vl_noop_handler
1811 #define vl_api_vnet_ip6_fib_counters_t_print vl_noop_handler
1812 #define vl_api_vnet_ip4_nbr_counters_t_endian vl_noop_handler
1813 #define vl_api_vnet_ip4_nbr_counters_t_print vl_noop_handler
1814 #define vl_api_vnet_ip6_nbr_counters_t_endian vl_noop_handler
1815 #define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler
1816
1817 static clib_error_t *
1818 stats_init (vlib_main_t * vm)
1819 {
1820   stats_main_t *sm = &stats_main;
1821   api_main_t *am = &api_main;
1822   void *vlib_worker_thread_bootstrap_fn (void *arg);
1823
1824   sm->vlib_main = vm;
1825   sm->vnet_main = vnet_get_main ();
1826   sm->interface_main = &vnet_get_main ()->interface_main;
1827   sm->api_main = am;
1828   sm->stats_poll_interval_in_seconds = 10;
1829   sm->data_structure_lock =
1830     clib_mem_alloc_aligned (sizeof (data_structure_lock_t),
1831                             CLIB_CACHE_LINE_BYTES);
1832   memset (sm->data_structure_lock, 0, sizeof (*sm->data_structure_lock));
1833
1834 #define _(N,n)                                                  \
1835     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1836                            vl_api_##n##_t_handler,              \
1837                            vl_noop_handler,                     \
1838                            vl_api_##n##_t_endian,               \
1839                            vl_api_##n##_t_print,                \
1840                            sizeof(vl_api_##n##_t), 0 /* do NOT trace! */);
1841   foreach_stats_msg;
1842 #undef _
1843
1844   /* tell the msg infra not to free these messages... */
1845   am->message_bounce[VL_API_VNET_INTERFACE_SIMPLE_COUNTERS] = 1;
1846   am->message_bounce[VL_API_VNET_INTERFACE_COMBINED_COUNTERS] = 1;
1847   am->message_bounce[VL_API_VNET_IP4_FIB_COUNTERS] = 1;
1848   am->message_bounce[VL_API_VNET_IP6_FIB_COUNTERS] = 1;
1849   am->message_bounce[VL_API_VNET_IP4_NBR_COUNTERS] = 1;
1850   am->message_bounce[VL_API_VNET_IP6_NBR_COUNTERS] = 1;
1851
1852   /*
1853    * Set up the (msg_name, crc, message-id) table
1854    */
1855   setup_message_id_table (am);
1856
1857   return 0;
1858 }
1859
1860 VLIB_INIT_FUNCTION (stats_init);
1861
1862 /* *INDENT-OFF* */
1863 VLIB_REGISTER_THREAD (stats_thread_reg, static) = {
1864   .name = "stats",
1865   .function = stats_thread_fn,
1866   .fixed_count = 1,
1867   .count = 1,
1868   .no_data_structure_clone = 1,
1869   .use_pthreads = 1,
1870 };
1871 /* *INDENT-ON* */
1872
1873 /*
1874  * fd.io coding-style-patch-verification: ON
1875  *
1876  * Local Variables:
1877  * eval: (c-set-style "gnu")
1878  * End:
1879  */